การเพิ่มความคิดเห็นแบบ Angular 2 @ViewChild จะไม่ส่งคืน


242

ฉันพยายามเรียนรู้ Angular 2

ฉันต้องการเข้าถึงองค์ประกอบลูกจากองค์ประกอบหลักโดยใช้@ViewChild Annotation

นี่คือบางส่วนของรหัส:

ในBodyContent.tsฉันมี:

import {ViewChild, Component, Injectable} from 'angular2/core';
import {FilterTiles} from '../Components/FilterTiles/FilterTiles';


@Component({
selector: 'ico-body-content'
, templateUrl: 'App/Pages/Filters/BodyContent/BodyContent.html'
, directives: [FilterTiles] 
})


export class BodyContent {
    @ViewChild(FilterTiles) ft:FilterTiles;

    public onClickSidebar(clickedElement: string) {
        console.log(this.ft);
        var startingFilter = {
            title: 'cognomi',
            values: [
                'griffin'
                , 'simpson'
            ]}
        this.ft.tiles.push(startingFilter);
    } 
}

ขณะที่อยู่ในFilterTiles.ts :

 import {Component} from 'angular2/core';


 @Component({
     selector: 'ico-filter-tiles'
    ,templateUrl: 'App/Pages/Filters/Components/FilterTiles/FilterTiles.html'
 })


 export class FilterTiles {
     public tiles = [];

     public constructor(){};
 }

ในที่สุดแม่แบบที่นี่ (ตามที่แนะนำในความคิดเห็น):

BodyContent.html

<div (click)="onClickSidebar()" class="row" style="height:200px; background-color:red;">
        <ico-filter-tiles></ico-filter-tiles>
    </div>

FilterTiles.html

<h1>Tiles loaded</h1>
<div *ngFor="#tile of tiles" class="col-md-4">
     ... stuff ...
</div>

FilterTiles.html เทมเพลตถูกโหลดอย่างถูกต้องใน แท็กico-filter-tiles (แน่นอนฉันสามารถเห็นส่วนหัว)

หมายเหตุ: คลาส BodyContent จะถูกฉีดเข้าไปในเทมเพลตอื่น (Body) โดยใช้ DynamicComponetLoader: dcl.loadAsRoot (BodyContent, '# ico-bodyContent', หัวฉีด):

import {ViewChild, Component, DynamicComponentLoader, Injector} from 'angular2/core';
import {Body}                 from '../../Layout/Dashboard/Body/Body';
import {BodyContent}          from './BodyContent/BodyContent';

@Component({
    selector: 'filters'
    , templateUrl: 'App/Pages/Filters/Filters.html'
    , directives: [Body, Sidebar, Navbar]
})


export class Filters {

    constructor(dcl: DynamicComponentLoader, injector: Injector) {
       dcl.loadAsRoot(BodyContent, '#ico-bodyContent', injector);
       dcl.loadAsRoot(SidebarContent, '#ico-sidebarContent', injector);

   } 
}

ปัญหาคือว่าเมื่อฉันพยายามที่จะเขียนftลงในบันทึกคอนโซลที่ฉันได้รับundefinedและแน่นอนฉันจะได้รับข้อยกเว้นเมื่อฉันพยายามที่จะผลักดันบางสิ่งบางอย่างภายใน "กระเบื้อง" อาร์เรย์: 'ไม่กระเบื้องอสังหาริมทรัพย์สำหรับ "ไม่ได้กำหนด'

อีกอย่างหนึ่ง: ดูเหมือนว่าส่วนประกอบตัวกรองจะโหลดอย่างถูกต้องเนื่องจากฉันสามารถเห็นแม่แบบ html ได้

ข้อเสนอแนะใด ๆ ขอบคุณ


ดูถูกต้องแล้ว อาจมีบางอย่างในเทมเพลต แต่ไม่รวมอยู่ในคำถามของคุณ
GünterZöchbauer

1
เห็นด้วยกับGünter ฉันสร้าง plunkr ด้วยรหัสของคุณและเทมเพลตที่เกี่ยวข้องอย่างง่ายและใช้งานได้ ดูที่ลิงค์นี้: plnkr.co/edit/KpHp5Dlmppzo1LXcutPV?p=preview เราต้องการเทมเพลต ;-)
Thierry Templier

1
ftจะไม่ถูกตั้งค่าในตัวสร้าง แต่ในตัวจัดการเหตุการณ์คลิกมันจะถูกตั้งค่าไว้แล้ว
GünterZöchbauer

5
คุณกำลังใช้loadAsRootงานซึ่งมีปัญหาที่ทราบแล้วเกี่ยวกับการตรวจจับการเปลี่ยนแปลง เพียงเพื่อให้แน่ใจลองใช้หรือloadNextToLocation loadIntoLocation
Eric Martinez

1
loadAsRootปัญหาก็คือ เมื่อฉันแทนที่ด้วยloadIntoLocationปัญหาได้รับการแก้ไข หากคุณแสดงความคิดเห็นเป็นคำตอบฉันสามารถทำเครื่องหมายเป็นที่ยอมรับได้
Andrea Ialenti

คำตอบ:


374

ฉันมีปัญหาที่คล้ายกันและคิดว่าฉันโพสต์ในกรณีที่คนอื่นทำผิดพลาดเหมือนกัน ประการแรกสิ่งหนึ่งที่ต้องพิจารณาคือAfterViewInit; @ViewChildคุณจำเป็นต้องรอให้มุมมองที่จะเริ่มต้นก่อนที่คุณจะสามารถเข้าถึงของคุณ อย่างไรก็ตามฉัน@ViewChildยังคงกลับมาเป็นโมฆะ *ngIfปัญหาก็คือของฉัน *ngIfสั่งถูกฆ่าองค์ประกอบการควบคุมของฉันดังนั้นฉันไม่สามารถอ้างอิงได้

import {Component, ViewChild, OnInit, AfterViewInit} from 'angular2/core';
import {ControlsComponent} from './controls/controls.component';
import {SlideshowComponent} from './slideshow/slideshow.component';

@Component({
    selector: 'app',
    template:  `
        <controls *ngIf="controlsOn"></controls>
        <slideshow (mousemove)="onMouseMove()"></slideshow>
    `,
    directives: [SlideshowComponent, ControlsComponent]
})

export class AppComponent {
    @ViewChild(ControlsComponent) controls:ControlsComponent;

    controlsOn:boolean = false;

    ngOnInit() {
        console.log('on init', this.controls);
        // this returns undefined
    }

    ngAfterViewInit() {
        console.log('on after view init', this.controls);
        // this returns null
    }

    onMouseMove(event) {
         this.controls.show();
         // throws an error because controls is null
    }
}

หวังว่าจะช่วย

แก้ไข
ดังกล่าวโดย@Ashg ด้านล่าง , การแก้ปัญหาคือการใช้แทน@ViewChildren@ViewChild


9
@kenecaswell คุณพบวิธีที่ดีกว่าในการแก้ปัญหาหรือไม่ ฉันกำลังเผชิญกับปัญหาเดียวกัน ฉันมี * ngIf จำนวนมากดังนั้นองค์ประกอบนั้นจะอยู่ในความเป็นจริงเท่านั้นหลังจากทั้งหมด แต่ฉันต้องการการอ้างอิงองค์ประกอบ วิธีใด ๆ ในการแก้ปัญหานี้>
โมนิกา

4
ฉันพบว่าองค์ประกอบลูกคือ 'ไม่ได้กำหนด' ใน ngAfterViewInit () หากใช้ ngIf ฉันพยายามใช้เวลานาน แต่ก็ไม่มีผล อย่างไรก็ตามองค์ประกอบย่อยมีให้บริการในภายหลัง (เช่นการตอบสนองต่อเหตุการณ์การคลิก ฯลฯ ) ถ้าฉันไม่ใช้ ngIf และถูกกำหนดตามที่คาดไว้ใน ngAfterViewInit () มีข้อมูลเพิ่มเติมเกี่ยวกับการสื่อสารระหว่างผู้ปกครอง / เด็กที่นี่angular.io/docs/ts/latest/cookbook/ …
Matthew Hegarty

3
ผมใช้บูต ngClass+ ชั้นแทนhidden ngIfที่ได้ผล ขอบคุณ!
Rahmathullah M

9
วิธีนี้ไม่ได้แก้ปัญหาใช้วิธีแก้ปัญหาด้านล่างโดยใช้ @ViewChildren ไปรับการอ้างอิงถึงการควบคุมเด็กเมื่อมันพร้อมใช้งาน
Ashg

20
นี่แค่พิสูจน์ "ปัญหา" ใช่ไหม มันไม่ได้โพสต์วิธีแก้ปัญหา
Miguel Ribeiro

146

ปัญหาดังกล่าวก่อนหน้านี้เป็นสิ่งngIfที่ทำให้มุมมองไม่ได้กำหนด คำตอบคือการใช้แทนViewChildren ViewChildฉันมีปัญหาที่คล้ายกันซึ่งฉันไม่ต้องการให้แสดงกริดจนกว่าจะโหลดข้อมูลอ้างอิงทั้งหมดแล้ว

HTML:

   <section class="well" *ngIf="LookupData != null">
       <h4 class="ra-well-title">Results</h4>
       <kendo-grid #searchGrid> </kendo-grid>
   </section>

รหัสส่วนประกอบ

import { Component, ViewChildren, OnInit, AfterViewInit, QueryList  } from '@angular/core';
import { GridComponent } from '@progress/kendo-angular-grid';

export class SearchComponent implements OnInit, AfterViewInit
{
    //other code emitted for clarity

    @ViewChildren("searchGrid")
    public Grids: QueryList<GridComponent>

    private SearchGrid: GridComponent

    public ngAfterViewInit(): void
    {

        this.Grids.changes.subscribe((comps: QueryList <GridComponent>) =>
        {
            this.SearchGrid = comps.first;
        });


    }
}

ที่นี่เรากำลังใช้ViewChildrenที่คุณสามารถฟังการเปลี่ยนแปลง ในกรณีนี้เด็ก ๆ #searchGridที่มีการอ้างอิง หวังว่านี่จะช่วยได้


4
ฉันต้องการเพิ่มในบางกรณีเมื่อคุณลองเปลี่ยนเช่น this.SearchGridคุณสมบัติที่คุณควรใช้ไวยากรณ์เช่นsetTimeout(()=>{ ///your code here }, 1); เพื่อหลีกเลี่ยงข้อยกเว้น: การแสดงออกมีการเปลี่ยนแปลงหลังจากที่ได้รับการตรวจสอบ
rafalkasa

3
คุณจะทำเช่นนี้ได้อย่างไรหากคุณต้องการวางแท็ก #searchGrid ของคุณในองค์ประกอบ HTML ปกติแทนที่จะเป็นองค์ประกอบ Angular2 (ตัวอย่างเช่น <div #searchGrid> </ div> และนี่คือภายในของบล็อก * ngIf?
เวิร์นเซ่น

1
นี่คือคำตอบที่ถูกต้องสำหรับกรณีการใช้งานของฉัน! ขอบคุณฉันต้องเข้าถึงองค์ประกอบตามที่มีให้ผ่าน ngIf =
Frozen_byte

1
งานนี้สมบูรณ์แบบสำหรับการตอบกลับ*ngIfอาแจ็กซ์ตอนนี้การทำงานและหลังจากการเรนเดอร์เราสามารถบันทึก ElementRef จากส่วนประกอบไดนามิค
elporfirio

4
นอกจากนี้อย่าลืมกำหนดให้การสมัครแล้วยกเลิกการสมัคร
tam.teixeira

64

คุณสามารถใช้ตัวตั้งค่าสำหรับ @ViewChild()

@ViewChild(FilterTiles) set ft(tiles: FilterTiles) {
    console.log(tiles);
};

หากคุณมี wrapper ngIf ตัวตั้งค่าจะถูกเรียกด้วย undefined และจากนั้นอีกครั้งพร้อมการอ้างอิงอีกครั้ง ngIf อนุญาตให้แสดงผลได้

ปัญหาของฉันเป็นอย่างอื่นแม้ว่า ฉันไม่ได้รวมโมดูลที่มี "FilterTiles" ใน app.modules แม่แบบไม่ได้เกิดข้อผิดพลาด แต่การอ้างอิงนั้นไม่ได้กำหนดไว้เสมอ


3
สิ่งนี้ไม่ได้ผลสำหรับฉัน - ฉันไม่ได้กำหนดครั้งแรก แต่ฉันไม่ได้รับสายที่สองพร้อมการอ้างอิง แอปคือ ng2 ... เป็นคุณลักษณะของ ng4 + หรือไม่
Jay Cummins

@Jay ฉันเชื่อว่านี่เป็นเพราะคุณยังไม่ได้ลงทะเบียนส่วนประกอบกับ Angular ในกรณีFilterTilesนี้ ฉันพบปัญหานั้นด้วยเหตุผลนั้นมาก่อน
รัฐสภา

1
ใช้งานได้กับ Angular 8 โดยใช้ #paginator บนองค์ประกอบ html และคำอธิบายประกอบเช่น@ViewChild('paginator', {static: false})
Qiteq

1
นี่เป็นการเรียกกลับสำหรับการเปลี่ยนแปลง ViewChild หรือไม่
Yasser Nascimento

24

สิ่งนี้ใช้ได้สำหรับฉัน

ตัวอย่างเช่นคอมโพเนนต์ของฉันชื่อ 'my-component' ถูกแสดงโดยใช้ * ngIf = "showMe" เช่นนั้น:

<my-component [showMe]="showMe" *ngIf="showMe"></my-component>

ดังนั้นเมื่อองค์ประกอบเริ่มต้นองค์ประกอบจะยังไม่ปรากฏจนกว่า "showMe" จะเป็นจริง ดังนั้นการอ้างอิง @ViewChild ของฉันจึงไม่ได้กำหนดทั้งหมด

นี่คือที่ฉันใช้ @ViewChildren และ QueryList ที่ส่งคืน ดูบทความเชิงมุมบน QueryList และการสาธิตการใช้งาน

คุณสามารถใช้ QueryList ที่ @ViewChildren ส่งคืนและสมัครสมาชิกการเปลี่ยนแปลงใด ๆ ของรายการอ้างอิงโดยใช้ rxjs ดังที่เห็นด้านล่าง @ViewChild ไม่มีความสามารถนี้

import { Component, ViewChildren, ElementRef, OnChanges, QueryList, Input } from '@angular/core';
import 'rxjs/Rx';

@Component({
    selector: 'my-component',
    templateUrl: './my-component.component.html',
    styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnChanges {

  @ViewChildren('ref') ref: QueryList<any>; // this reference is just pointing to a template reference variable in the component html file (i.e. <div #ref></div> )
  @Input() showMe; // this is passed into my component from the parent as a    

  ngOnChanges () { // ngOnChanges is a component LifeCycle Hook that should run the following code when there is a change to the components view (like when the child elements appear in the DOM for example)
    if(showMe) // this if statement checks to see if the component has appeared becuase ngOnChanges may fire for other reasons
      this.ref.changes.subscribe( // subscribe to any changes to the ref which should change from undefined to an actual value once showMe is switched to true (which triggers *ngIf to show the component)
        (result) => {
          // console.log(result.first['_results'][0].nativeElement);                                         
          console.log(result.first.nativeElement);                                          

          // Do Stuff with referenced element here...   
        } 
      ); // end subscribe
    } // end if
  } // end onChanges 
} // end Class

หวังว่านี่จะช่วยให้ใครบางคนประหยัดเวลาและความยุ่งยาก


3
แน่นอนว่าทางออกของคุณน่าจะเป็นวิธีที่ดีที่สุดที่ระบุไว้ หมายเหตุเราต้องจำไว้ว่าการแก้ปัญหา 73 อันดับแรกนั้นเลิกใช้แล้ว ... เนื่องจากคำสั่ง: [... ] การประกาศไม่ได้รับการสนับสนุนในเชิงมุมอีกต่อไป 4. IOW มันจะไม่ทำงานในสถานการณ์เชิงมุม 4
PeteZaria

5
อย่าลืมยกเลิกการสมัครหรือใช้.take(1).subscribe()แต่คำตอบที่ยอดเยี่ยมขอบคุณมาก!
แบลร์คอนนอลลี่

2
ทางออกที่ดีเยี่ยม ฉันสมัครรับข้อมูลการอ้างอิงใน ngAfterViewInit () มากกว่า ngOnChanges () แต่ฉันต้องเพิ่ม setTimeout เพื่อกำจัดข้อผิดพลาด ExpressionChangedAfterChecked
Josf

ควรทำเครื่องหมายเป็นโซลูชันจริง ขอบคุณมาก!
platzhersh

10

วิธีแก้ปัญหาของฉันคือการใช้แทน[style.display]="getControlsOnStyleDisplay()" *ngIf="controlsOn"บล็อกอยู่ที่นั่น แต่ไม่ปรากฏขึ้น

@Component({
selector: 'app',
template:  `
    <controls [style.display]="getControlsOnStyleDisplay()"></controls>
...

export class AppComponent {
  @ViewChild(ControlsComponent) controls:ControlsComponent;

  controlsOn:boolean = false;

  getControlsOnStyleDisplay() {
    if(this.controlsOn) {
      return "block";
    } else {
      return "none";
    }
  }
....

มีหน้าที่แสดงรายการของรายการในตารางหรือแสดงรายการแก้ไขตามค่าของตัวแปร showList กำจัดข้อผิดพลาดของคอนโซลที่น่ารำคาญโดยใช้ [style.display] = "! showList", รวมกับ * ngIf = "! showList"
razvanone

9

สิ่งที่แก้ปัญหาของฉันคือการทำให้แน่ใจว่าถูกกำหนดให้staticfalse

@ViewChild(ClrForm, {static: false}) clrForm;

เมื่อstaticปิดใช้งานการ@ViewChildอ้างอิงจะได้รับการอัปเดตโดย Angular เมื่อ*ngIfคำสั่งเปลี่ยนแปลง


1
นี่เป็นผู้ตอบที่สมบูรณ์แบบเพียงแค่ชี้ไปที่เป็นบทสนทนาที่ดีในการตรวจสอบค่า nullable ดังนั้นเราจึงลงท้ายด้วยบางสิ่งเช่นนี้: @ViewChild (ClrForm, {static: false}) ชุด clrForm (clrForm: ClrForm) {ถ้า (clrForm) {this.clrForm = clrForm; }};
Klaus Klein

ฉันลองหลายสิ่งหลายอย่างและในที่สุดก็พบว่าสิ่งนี้เป็นตัวการ
Manish Sharma

8

วิธีการแก้ปัญหาของฉันไปนี้คือการแทนที่ด้วย*ngIf [hidden]ข้อเสียคือองค์ประกอบของเด็กทั้งหมดมีอยู่ในรหัส DOM แต่ทำงานตามความต้องการของฉัน


5

ในกรณีของฉันฉันมี setter ตัวแปรอินพุตโดยใช้ViewChild, และViewChildอยู่ภายใน*ngIfคำสั่งดังนั้น setter จึงพยายามเข้าถึงก่อนที่เรนเดอร์*ngIf(มันจะทำงานได้ดีหากไม่มี*ngIfแต่จะไม่ทำงานหากตั้งค่าเป็น จริงด้วย*ngIf="true")

เพื่อแก้ปัญหาฉันใช้ Rxjs เพื่อให้แน่ใจว่ามีการอ้างอิงถึงการViewChildรอจนกว่าจะเริ่มต้นมุมมอง ก่อนอื่นให้สร้างหัวเรื่องที่เสร็จสมบูรณ์เมื่อหลังจากดูแล้ว

export class MyComponent implements AfterViewInit {
  private _viewInitWaiter$ = new Subject();

  ngAfterViewInit(): void {
    this._viewInitWaiter$.complete();
  }
}

จากนั้นสร้างฟังก์ชั่นที่รับและประมวลผลแลมบ์ดาหลังจากที่ผู้ทดสอบเสร็จสิ้น

private _executeAfterViewInit(func: () => any): any {
  this._viewInitWaiter$.subscribe(null, null, () => {
    return func();
  })
}

ขั้นสุดท้ายตรวจสอบให้แน่ใจว่าการอ้างอิงกับ ViewChild ใช้ฟังก์ชันนี้

@Input()
set myInput(val: any) {
    this._executeAfterViewInit(() => {
        const viewChildProperty = this.viewChild.someProperty;
        ...
    });
}

@ViewChild('viewChildRefName', {read: MyViewChildComponent}) viewChild: MyViewChildComponent;

1
นี่เป็นวิธีที่ดีกว่าการแก้ปัญหาที่เกิดขึ้นทั้งหมด
เลียม

4

มันจะต้องทำงาน

แต่ตามที่GünterZöchbauerกล่าวว่าต้องมีปัญหาอื่น ๆ ในแม่แบบ ผมได้สร้างนะที่เกี่ยวข้อง-Plunkr คำตอบ โปรดตรวจสอบคอนโซลของเบราว์เซอร์

boot.ts

@Component({
selector: 'my-app'
, template: `<div> <h1> BodyContent </h1></div>

      <filter></filter>

      <button (click)="onClickSidebar()">Click Me</button>
  `
, directives: [FilterTiles] 
})


export class BodyContent {
    @ViewChild(FilterTiles) ft:FilterTiles;

    public onClickSidebar() {
        console.log(this.ft);

        this.ft.tiles.push("entered");
    } 
}

filterTiles.ts

@Component({
     selector: 'filter',
    template: '<div> <h4>Filter tiles </h4></div>'
 })


 export class FilterTiles {
     public tiles = [];

     public constructor(){};
 }

มันทำงานเหมือนจับใจ โปรดตรวจสอบแท็กและการอ้างอิงของคุณอีกครั้ง

ขอบคุณ ...


1
หากปัญหาเป็นเช่นเดียวกับของฉันการทำซ้ำคุณจะต้องใส่ * ngIf ในเทมเพลตโดยรอบ <filter> </filter> .. เห็นได้ชัดว่าถ้า ngIf ส่งกลับค่าเท็จ ViewChild จะไม่ได้รับสายและส่งคืน null
Dan Chase

2

สิ่งนี้ใช้งานได้สำหรับฉันดูตัวอย่างด้านล่าง

import {Component, ViewChild, ElementRef} from 'angular2/core';

@Component({
    selector: 'app',
    template:  `
        <a (click)="toggle($event)">Toggle</a>
        <div *ngIf="visible">
          <input #control name="value" [(ngModel)]="value" type="text" />
        </div>
    `,
})

export class AppComponent {

    private elementRef: ElementRef;
    @ViewChild('control') set controlElRef(elementRef: ElementRef) {
      this.elementRef = elementRef;
    }

    visible:boolean;

    toggle($event: Event) {
      this.visible = !this.visible;
      if(this.visible) {
        setTimeout(() => { this.elementRef.nativeElement.focus(); });
      }
    }

}


2

ฉันมีปัญหาที่คล้ายกันซึ่งViewChildอยู่ในส่วนswitchคำสั่งที่ไม่โหลดองค์ประกอบ viewChild ก่อนที่จะถูกอ้างอิง ฉันแก้ไขมันด้วยวิธีกึ่งแฮ็ก แต่ห่อการViewChildอ้างอิงในสิ่งsetTimeoutที่ถูกดำเนินการทันที (เช่น 0ms)


1

ทางออกของฉันคือการย้าย ngIf จากด้านนอกขององค์ประกอบลูกไปที่ด้านในขององค์ประกอบเด็กใน div ที่ห่อทั้งส่วนของ html ด้วยวิธีนี้มันยังคงถูกซ่อนอยู่เมื่อมันต้องการ แต่ก็สามารถโหลดส่วนประกอบและฉันสามารถอ้างอิงในผู้ปกครอง


แต่สำหรับสิ่งนั้นคุณได้รับตัวแปร "ที่มองเห็น" ซึ่งอยู่ในพาเรนต์ได้อย่างไร
Dan Chase

1

ฉันแก้ไขมันเพียงแค่เพิ่ม SetTimeout หลังจากตั้งค่าองค์ประกอบที่มองเห็นได้

HTML ของฉัน:

<input #txtBus *ngIf[show]>

My Component JS

@Component({
  selector: "app-topbar",
  templateUrl: "./topbar.component.html",
  styleUrls: ["./topbar.component.scss"]
})
export class TopbarComponent implements OnInit {

  public show:boolean=false;

  @ViewChild("txtBus") private inputBusRef: ElementRef;

  constructor() {

  }

  ngOnInit() {}

  ngOnDestroy(): void {

  }


  showInput() {
    this.show = true;
    setTimeout(()=>{
      this.inputBusRef.nativeElement.focus();
    },500);
  }
}

1

ในกรณีของฉันฉันรู้ว่าองค์ประกอบของเด็กจะมีอยู่เสมอ แต่ต้องการที่จะเปลี่ยนสถานะก่อนที่เด็กเริ่มต้นเพื่อบันทึกงาน

ฉันเลือกที่จะทดสอบสำหรับเด็กจนกว่าจะปรากฏขึ้นและทำการเปลี่ยนแปลงทันทีซึ่งช่วยฉันรอบการเปลี่ยนแปลงในองค์ประกอบของเด็ก

export class GroupResultsReportComponent implements OnInit {

    @ViewChild(ChildComponent) childComp: ChildComponent;

    ngOnInit(): void {
        this.WhenReady(() => this.childComp, () => { this.childComp.showBar = true; });
    }

    /**
     * Executes the work, once the test returns truthy
     * @param test a function that will return truthy once the work function is able to execute 
     * @param work a function that will execute after the test function returns truthy
     */
    private WhenReady(test: Function, work: Function) {
        if (test()) work();
        else setTimeout(this.WhenReady.bind(window, test, work));
    }
}

Alertnatively setTimeoutคุณสามารถเพิ่มจำนวนสูงสุดของความพยายามหรือเพิ่มไม่กี่มิลลิวินาทีล่าช้าไป setTimeoutได้อย่างมีประสิทธิภาพโยนฟังก์ชั่นไปที่ด้านล่างของรายการของการดำเนินการที่ค้างอยู่


0

วิธีการทั่วไปประเภทหนึ่ง:

คุณสามารถสร้างวิธีการที่จะรอจนกว่าViewChildจะพร้อม

function waitWhileViewChildIsReady(parent: any, viewChildName: string, refreshRateSec: number = 50, maxWaitTime: number = 3000): Observable<any> {
  return interval(refreshRateSec)
    .pipe(
      takeWhile(() => !isDefined(parent[viewChildName])),
      filter(x => x === undefined),
      takeUntil(timer(maxWaitTime)),
      endWith(parent[viewChildName]),
      flatMap(v => {
        if (!parent[viewChildName]) throw new Error(`ViewChild "${viewChildName}" is never ready`);
        return of(!parent[viewChildName]);
      })
    );
}


function isDefined<T>(value: T | undefined | null): value is T {
  return <T>value !== undefined && <T>value !== null;
}

การใช้งาน:

  // Now you can do it in any place of your code
  waitWhileViewChildIsReady(this, 'yourViewChildName').subscribe(() =>{
      // your logic here
  })

0

สำหรับฉันปัญหาคือฉันอ้างอิงรหัสในองค์ประกอบ

@ViewChild('survey-form') slides:IonSlides;

<div id="survey-form"></div>

แทนที่จะเป็นเช่นนี้:

@ViewChild('surveyForm') slides:IonSlides;

<div #surveyForm></div>

0

หากคุณใช้ Ionic คุณจะต้องใช้ionViewDidEnter()วงจรเบ็ดเสร็จ อิออนวิ่งบางสิ่งเพิ่มเติม (ส่วนใหญ่เป็นภาพเคลื่อนไหวที่เกี่ยวข้อง) ซึ่งมักจะเกิดข้อผิดพลาดที่ไม่คาดคิดเช่นนี้จึงจำเป็นที่จะต้องมีอะไรบางอย่างที่วิ่งหลัง ngOnInit , ngAfterContentInitและอื่น ๆ


-1

นี่คือสิ่งที่เหมาะกับฉัน

@ViewChild('mapSearch', { read: ElementRef }) mapInput: ElementRef;

ngAfterViewInit() {
  interval(1000).pipe(
        switchMap(() => of(this.mapInput)),
        filter(response => response instanceof ElementRef),
        take(1))
        .subscribe((input: ElementRef) => {
          //do stuff
        });
}

ดังนั้นผมจึงตั้งพื้นมีการตรวจสอบทุกวินาทีจนกลายเป็นความจริงแล้วฉันทำสิ่งที่ฉันเกี่ยวข้องกับ*ngIfElementRef


-3

วิธีแก้ปัญหาที่ใช้งานได้สำหรับฉันคือการเพิ่มคำสั่งในการประกาศใน app.module.ts

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.