首页 文章

检查子组件的属性

提问于
浏览
1

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

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

我已经尝试使用 @ViewChild decorator与这样的东西 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>

EDIT #2:

HTML父母:

<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 回答

  • 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 指向 StepOneComponent 中的属性 files . 为了让自己设置一个更改侦听器,您需要做的就是利用 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
        });
    }
    

相关问题