检查子组件的属性

时间:2017-11-02 19:18:26

标签: javascript angular typescript

我的子组件中有一个名为files的属性,它是input type=file。使用该属性,我可以检查用户是否选择了要上传的文件,因此如果输入中没有文件,我可以禁用提交按钮。 我的问题是,现在我在父组件中有提交按钮,因此当输入中有文件时,我无法检查属性files以启用按钮。

那么,如何从父组件访问files属性并检查输入是否包含文件?

我已经尝试使用@ViewChild装饰器使用类似this.childComponent.files之类的内容,但我未定义。我也试过了@Output,但我无法让它发挥作用。

编辑:添加代码

父组件TS:

import { Component, OnInit, ViewChild } from '@angular/core';
import { StepState } from '@covalent/core';
import { StepThreeComponent } from './step-three/step-three.component';
import { StepTwoComponent } from './step-two/step-two.component';
import { StepOneComponent } from './step-one/step-one.component';

@Component({
  selector: 'app-user-verification',
  templateUrl: './user-verification.component.html',
  styleUrls: ['./user-verification.component.scss']
})
export class UserVerificationComponent implements OnInit {

  @ViewChild(StepThreeComponent) stepThreeComponent: StepThreeComponent;
  @ViewChild(StepTwoComponent) stepTwoComponent: StepTwoComponent;
  @ViewChild(StepOneComponent) stepOneComponent: StepOneComponent;

  checkFiles: any;

  //EXECUTE ALL UPLOAD FUNCTIONS

  uploadAllFiles() {
    this.stepThreeComponent.uploadSingle();
    this.stepOneComponent.uploadSingle();
    this.stepTwoComponent.uploadSingle();
    console.log('No FUnciona')
  }

  ngOnInit() {
    this.checkFiles = this.stepOneComponent.files;
    console.log(this.checkFiles)
  }

}

父组件HTML:

<td-steps>
  <td-step #step1 sublabel="Suba una copia de la parte delantera de su Cédula de Identidad" [active]="true" [disabled]="false" (activated)="activeStep1Event()" [state]="stateStep1" (deactivated)="deactiveStep1Event()">
      <ng-template #head1 td-step-label><span>Foto de Cédula de Identidad (I)</span></ng-template>
      <app-step-one></app-step-one>
  </td-step>
  <td-step #step2 [active]="true" label="Foto de Cédula de Identidad (II)" sublabel="Suba una copia de la parte trasera de su Cédula de Identidad" [state]="stateStep2" [disabled]="false" disableRipple> 
      <app-step-two></app-step-two>
  </td-step>
  <td-step #step3 label="Selfie con Cédula de Identidad" sublabel="Subir Selfie" [state]="stateStep3" [disabled]="true" [active]="true">
      <app-step-three></app-step-three>
      <ng-template td-step-actions>
          <button [disabled]="!checkFiles" (click)="uploadAllFiles()">SUBIR TODO</button>
      </ng-template>
  </td-step>
</td-steps>

儿童组成部分TS:

import { Component } from '@angular/core';
import { UploadService } from '../../../services/upload.service';
import * as firebase from 'firebase/app';
import "firebase/storage";
import { Upload } from '../../../services/upload';
import * as _ from "lodash";

@Component({
  selector: 'app-step-one',
  templateUrl: './step-one.component.html',
  styleUrls: ['./step-one.component.scss']
})
export class StepOneComponent {

  selectedFiles: FileList;
  currentUpload: Upload;
  files: any;
  disabled: any;

  constructor(private upSvc: UploadService) { }

    //Upload Service
    detectFiles(event) {
      this.selectedFiles = event.target.files;
  }

  uploadSingle() {
    let uid = firebase.auth().currentUser.uid;
    let file = this.files
    this.currentUpload = new Upload(file);
    this.upSvc.pushUpload(this.currentUpload, uid)
  }

}

儿童组件HTML:

<div>
    <div layout="row">
        <md-input-container tdFileDrop [disabled]="disabled" (fileDrop)="files = $event" (click)="fileInput.inputElement.click()" (keyup.enter)="fileInput.inputElement.click()" (keyup.delete)="fileInput.clear()" (keyup.backspace)="fileInput.clear()" flex>
            <input mdInput placeholder="select or drop files" [value]="files?.length ? (files?.length + ' files') : files?.name" [disabled]="disabled" readonly/>
        </md-input-container>
        <button md-icon-button *ngIf="files" (click)="fileInput.clear()" (keyup.enter)="fileInput.clear()">
            <md-icon>cancel</md-icon>
        </button>
        <td-file-input class="push-left-sm push-right-sm" #fileInput [(ngModel)]="files" multiple [disabled]="disabled">
            <md-icon>folder</md-icon>
            <span class="text-upper">Browse...</span>
        </td-file-input>
    </div>

    <div *ngIf="currentUpload">
        <div class="progress">
            <div class="progress-bar progress-bar-animated" [ngStyle]="{ 'width': currentUpload?.progress + '%' }"></div>
        </div>
        Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete
    </div>
</div>

编辑#2:

html parent:

<app-step-one #StepOneComponent></app-step-one>
父母:

  ngAfterViewInit() {
    this.checkFiles = this.stepOneComponent.getFiles();
  }

  getFiles() {
   this.checkFiles = this.stepOneComponent.getFiles();
    console.log(this.checkFiles);
  }

ts孩子补充说:

  getFiles() {
    return this.files
    }

我也试过了this.stepOneComponent.files;,但它也没有用。之后我在子组件中创建getFiles()以查看是否可以在输入中获取文件并且它有效。我能够从父组件中执行console.log()并获取文件数据。但我需要做的是自动检查更改,而不是使用按钮内的功能。

编辑#3(重复): 这个问题对我没有帮助,因为我需要自动检查更改而不是手动检查。

1 个答案:

答案 0 :(得分:1)

为您的孩子课程提​​供一些公共元素,例如isValid,可能还有一些$valid: BehaviorSubject<boolean>可以通过父组件内的@ViewChild消费。

@Component({ 
    selector: 'parent-component',
    template: '<child-component #childComponent></child-component>'
})
export class ParentComponent implements AfterViewInit {
    @ViewChild('childComponent') childComponent: ChildComponent;

    ngAfterViewInit(): void {
        // do stuff here otherwise your @ViewChildren might not be available
        this.childComponent.$valid.subscribe(isValid => {
             // do validity related things
        })
    }
}

编辑:看到你的代码后,我相信你的@ViewChild实现是不正确的:

@ViewChild(StepOneComponent) stepOneComponent: StepOneComponent;

什么都不做。装饰器在用#StepOneComponent装饰的父组件标记中找不到任何内容。

在您的标记中,您需要:

<app-step-one #StepOneComponent></app-step-one>

在你的班上:

@ViewChild('StepOneComponent') stepOneComponent: StepOneComponent;

编辑更多内容:

@ViewChild('thisIsADecorator') someVariableName: SomeComponentClass;

是指向:

的指针
<child-component #thisIsADecorator></child-component>

哪个是SomeComponentClass的实例,其选择器为child-component。您需要使用AfterViewInit的原因是子组件将不可用; from Angular's docs

  

ngAfterViewInit()在Angular初始化组件的视图和子视图后做出响应。

在您的父类中,@ViewChild(StepOneComponent) stepOneComponent: StepOneComponent;没有做任何事情。

您的父母标记:

<td-steps>
  <td-step #step1 sublabel="Suba una copia de la parte delantera de su Cédula de Identidad" [active]="true" [disabled]="false" (activated)="activeStep1Event()" [state]="stateStep1" (deactivated)="deactiveStep1Event()">
      <ng-template #head1 td-step-label><span>Foto de Cédula de Identidad (I)</span></ng-template>
      <app-step-one></app-step-one>// <---- This is the view child you're trying to get a handle on, but there is nothing for Angular to use to find it
  </td-step>

如果你解决了这些问题,那么你所做的就会奏效。

用于暴露属性getter的编辑

这里的诀窍在于,您td-file-inputngModel绑定了files中的属性StepOneComponent。为了让自己适应变化倾听者,您需要做的就是利用files的getter / setter。

private _files: any;
private $_files: BehaviorSubject<any> = new BehaviorSubject();

public get files(): any {
    return this._files;
}

public set files(val: any) {
    this.$_files.next(this._files = val);
}

public get $files(): Observable<any> {
    return this.$_files.asObservable();
}

然后在您的父组件中:

ngAfterViewInit() { // <-- I would really switch to this from ngOnInit
    this.stepOneComponent.$files.subscribe((newVal: any) => {
        console.log(newVal);
        // do stuff
    });
}