typescript - कोणीय 2 @ViewChild एनोटेशन अपरिभाषित देता है




angular (12)

आप @ViewChild() लिए एक सेटर का उपयोग कर सकते हैं

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

यदि आपके पास एक एनजीआई आवरण है, तो सेटर को अपरिभाषित कहा जाएगा, और फिर एक बार एक संदर्भ के साथ एनजीआईएफ इसे प्रस्तुत करने की अनुमति देता है।

मेरा मुद्दा हालांकि कुछ और था। मैंने अपने app.modules में अपने "FilterTiles" वाले मॉड्यूल को शामिल नहीं किया था। टेम्पलेट में कोई त्रुटि नहीं थी, लेकिन संदर्भ हमेशा अपरिभाषित था।

मैं कोणीय 2 सीखने की कोशिश कर रहा हूं।

मैं @ViewChild एनोटेशन का उपयोग करके एक मूल घटक से एक बच्चे के घटक तक पहुंचना चाहूंगा

यहाँ कोड की कुछ पंक्तियाँ हैं:

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- टाइल्स टैग में लोड किया गया है (वास्तव में मैं हेडर देखने में सक्षम हूं)।

नोट: BodyContent वर्ग को डायनामिककंपोनिटलोडर का उपयोग करके एक अन्य टेम्पलेट (बॉडी) के अंदर इंजेक्ट किया जाता है: 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 हो जाता हूं, और निश्चित रूप से मुझे एक अपवाद मिलता है जब मैं "टाइल" सरणी के अंदर कुछ धक्का देने की कोशिश करता हूं: '' अपरिभाषित '' के लिए कोई संपत्ति टाइल नहीं

एक और बात: FilterTiles घटक सही ढंग से भरा हुआ लगता है, क्योंकि मैं इसके लिए html टेम्पलेट देख पा रहा हूं।

कोई उपाय? धन्यवाद


इसका मेरा समाधान यह था कि बच्चे के कंपोनेंट के बाहर से एनजीआईएफ को स्थानांतरित करने के लिए चाइल्ड कंपोनेंट के अंदर एक डिव पर जो html के पूरे सेक्शन को लपेटता है। इस तरह यह तब भी छिपा हुआ था जब इसकी आवश्यकता थी, लेकिन घटक को लोड करने में सक्षम था और मैं इसे माता-पिता में संदर्भित कर सकता था।


इसने मेरे लिए काम किया।

उदाहरण के लिए, 'माय-कंपोनेंट' नाम के मेरे घटक को * ngIf = "showMe" का उपयोग करके प्रदर्शित किया गया था:

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

इसलिए, जब घटक को इनिशियलाइज़ किया जाता है तब तक घटक को तब तक प्रदर्शित नहीं किया जाता है जब तक कि "showMe" सत्य न हो। इस प्रकार, मेरे @ दृश्यदर्शी संदर्भ सभी अपरिभाषित थे।

यह वह जगह है जहां मैंने @ViewChildren और QueryList का उपयोग किया है जो इसे लौटाता है। QueryList पर कोणीय लेख और @ViewChildren का उपयोग डेमो देखें

आप QueryList का उपयोग कर सकते हैं जो @ViewChildren रिटर्न करता है और नीचे दिए गए अनुसार rxjs का उपयोग करके संदर्भित आइटम में किसी भी परिवर्तन की सदस्यता लेता है। @ दृश्यचिल्ड में यह क्षमता नहीं है।

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

आशा है कि यह किसी को कुछ समय और हताशा को बचाने में मदद करता है।


एक प्रकार का सामान्य दृष्टिकोण:

आप एक विधि बना सकते हैं जो तब तक प्रतीक्षा करेगी जब तक 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
  })

मेरा वर्कअराउंड का उपयोग करना था [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";
    }
  }
....

मेरे पास एक समान मुद्दा था और मुझे लगा कि अगर कोई और गलती करता है तो मैं पोस्ट करूंगा। सबसे पहले, एक बात पर विचार करना है 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 बजाय @ViewChildren का उपयोग @ViewChild


मेरे मामले में, मेरे पास ViewChild का उपयोग करके एक इनपुट वैरिएबल सेटर था, और ViewChild एक *ngIf निर्देश के अंदर था, इसलिए सेटर *ngIf रेंडर करने से पहले इसे एक्सेस करने की कोशिश कर रहा था (यह *ngIf बिना ठीक काम करेगा, लेकिन होगा) काम नहीं है अगर यह हमेशा सच के साथ सेट था *ngIf="true"

हल करने के लिए, मैंने Rxjs का उपयोग यह सुनिश्चित करने के लिए किया कि व्यूहिल्ड के किसी भी संदर्भ का तब तक इंतजार किया जाए जब तक कि दृश्य शुरू न हो जाए। सबसे पहले, एक विषय बनाएँ, जो देखने के बाद जब पूरा हो जाए।

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;

मेरे लिए काम करने वाला समाधान app.module.ts में घोषणाओं में निर्देश जोड़ना था


मैं इसे ठीक से सेटटाइमआउट जोड़ रहा हूँ सेट के बाद घटक दिखाई दे रहा है

मेरा HTML:

<input #txtBus *ngIf[show]>

मेरे घटक जेएस

@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);
  }
}

यह काम करना होगा।

लेकिन जैसा कि गुंटर ज़ोचबॉयर ने कहा कि टेम्पलेट में कुछ अन्य समस्या होनी चाहिए। मैंने थोड़े Relevant-Plunkr-Answer बनाए हैं। प्लीज ब्राउजर के कंसोल को चेक करें।

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(){};
 }

यह एक सम्मोहन की तरह काम करता है। कृपया अपने टैग और संदर्भों को दोबारा जांचें।

धन्यवाद...


यहाँ कुछ है कि मेरे लिए काम किया है।

@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
        });
}

इसलिए मैं मूल रूप से हर सेकंड एक चेक सेट करता हूं जब तक कि *ngIf सही नहीं हो जाता है और तब मैं अपना सामान ElementRef से संबंधित करता ElementRef


मेरे मामले में, मुझे पता था कि बच्चा घटक हमेशा मौजूद रहेगा, लेकिन काम को बचाने के लिए बच्चे को शुरू करने से पहले राज्य को बदलना चाहता था।

मैं तब तक बच्चे के लिए परीक्षण करना चुनता हूं जब तक वह दिखाई नहीं देता और तुरंत परिवर्तन करता है, जिसने मुझे बच्चे के घटक पर एक परिवर्तन चक्र बचाया।

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));
    }
}

सचेत रूप से, आप अधिकतम प्रयासों को जोड़ सकते हैं या सेटटाइमआउट में कुछ ms विलंब जोड़ सकते हैं। setTimeout प्रभावी रूप से फ़ंक्शन को लंबित संचालन की सूची के नीचे फेंक देता है।







angular