typescript - কৌণিক 2 @ ভিউচিল্ড টীকাটি অপরিবর্তিত




angular (12)

আমি কৌনিক 2 শিখার চেষ্টা করছি।

আমি @ ভিউচিল্ড এনোটেশন ব্যবহার করে পিতামাতাদের উপাদান থেকে কোনও সন্তানের অংশে অ্যাক্সেস করতে চাই।

কোডের কয়েকটি লাইন এখানে:

বডি কনটেন্ট.সেটে আমার রয়েছে:

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

ফিল্টার টাইলস.টেসে থাকাকালীন :

 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>

ফিল্টারটাইলস html টেমপ্লেটটি সঠিকভাবে আইকো -ফিল্টার-টাইলস ট্যাগে লোড করা হয়েছে (প্রকৃতপক্ষে আমি শিরোনামটি দেখতে সক্ষম)

দ্রষ্টব্য: বডি কনটেন্ট ক্লাসটি ডায়নামিক কমপোনেটলোডার ব্যবহার করে অন্য টেমপ্লেটের (বডি) ভিতরে ইনজেকশনের ব্যবস্থা করা হয়েছে: dcl.loadAsRoot (বডি-কনটেন্ট, '# আইকো-বডি-কনটেন্ট', ইনজেক্টর):

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 লেখার চেষ্টা করি তখন আমি ft হয়ে যাই এবং অবশ্যই যখন আমি "টাইলস" অ্যারের ভিতরে কোনও কিছুর চেষ্টা করি তখন ব্যতিক্রম পাই: '"অপরিজ্ঞাত" "এর জন্য কোনও সম্পত্তি টাইলস নেই

আরও একটি জিনিস: ফিল্টারটাইলস উপাদানটি সঠিকভাবে লোড হয়েছে বলে মনে হচ্ছে, যেহেতু আমি এর জন্য এইচটিএমএল টেমপ্লেটটি দেখতে পাচ্ছি।

যেকোনো পরামর্শ? ধন্যবাদ


আপনি @ViewChild() জন্য একটি সেটার ব্যবহার করতে পারেন

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

আপনার যদি এনজিআইএফ র‌্যাপার থাকে তবে সেটারটি অপরিজ্ঞাত করে কল করা হবে, এবং আবার একবার রেফারেন্স সহ এনজিআইএফ এটি রেন্ডার করতে দেয়।

আমার বিষয়টি যদিও অন্য কিছু ছিল। আমি আমার অ্যাপ্লিকেশনগুলির মডেলগুলিতে আমার "ফিল্টার টাইলস" মডিউলটি অন্তর্ভুক্ত করি নি। টেমপ্লেটটি কোনও ত্রুটি ফেলেনি তবে রেফারেন্সটি সর্বদা সংজ্ঞায়িত ছিল।


আমার [style.display]="getControlsOnStyleDisplay()" ছিল [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";
    }
  }
....

আমার একটি অনুরূপ সমস্যা ছিল, যেখানে ViewChild একটি switch ধারাটির অভ্যন্তরে ছিল যা রেফারেন্স হওয়ার আগে ভিউচিল্ড উপাদানটি লোড করে না। আমি এটি একটি অর্ধ-হ্যাকি উপায়ে সমাধান করেছি তবে তাত্ক্ষণিকভাবে কার্যকর করা একটি setTimeout ViewChild রেফারেন্সটি মোড়ানো (যেমন 0 মিমি)


আমার ক্ষেত্রে, ViewChild ব্যবহার করে আমার একটি ইনপুট ভেরিয়েবল সেটার ছিল এবং ViewChild একটি *ngIf নির্দেশের ভিতরে ছিল, সুতরাং *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();
  })
}

শেষ পর্যন্ত, ভিউচিল্ডের উল্লেখগুলি এই ফাংশনটি ব্যবহার করে তা নিশ্চিত করুন।

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

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

আমার সমাধানটি *ngIf [hidden] দিয়ে *ngIf প্রতিস্থাপন করা। ডাউনসাইডটি ছিল কোড ডিওমে সমস্ত শিশু উপাদান উপস্থিত ছিল। কিন্তু আমার প্রয়োজনীয়তা জন্য কাজ।


আমারও একই সমস্যা ছিল এবং আমি ভেবেছিলাম যে অন্য কেউ যদি একই ভুল করে তবে আমি পোস্ট করব। প্রথমত, বিবেচনার জন্য একটি বিষয় হ'ল AfterViewInit ; আপনি নিজের @ViewChild অ্যাক্সেস করতে @ViewChild আগে আপনাকে @ViewChild শুরু করার জন্য অপেক্ষা করতে হবে। যাইহোক, আমার @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
    }
}

আশা করি এইটি কাজ করবে.

সম্পাদনা
নীচে @ অ্যাসগ দ্বারা উল্লিখিত হিসাবে, একটি সমাধান হ'ল @ViewChildren পরিবর্তে @ViewChild @ViewChildren ব্যবহার করা।


এক ধরণের জেনেরিক পদ্ধতির:

আপনি এমন একটি পদ্ধতি তৈরি করতে পারেন যা 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
  })

এখানে আমার জন্য কাজ করে এমন কিছু।

@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


এটি আমার পক্ষে কাজ করে, নীচের উদাহরণটি দেখুন।

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

}


এটি আমার পক্ষে কাজ করেছে।

উদাহরণস্বরূপ, 'আমার-উপাদান' নামক আমার উপাদানটি * ngIf = "showMe" ব্যবহার করে প্রদর্শিত হয়েছিল:

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

সুতরাং, যখন উপাদানটি ইনিশিয়াল করা হবে তখন "শোএমই" সত্য না হওয়া অবধি উপাদানটি প্রদর্শিত হবে না। সুতরাং, আমার @ ভিউচিল্ড রেফারেন্সগুলি সমস্ত অপরিজ্ঞাত ছিল।

এখানেই আমি @ ভিউচিল্ডেন এবং কোয়েরিস্ট ব্যবহার করেছি যা এটি ফিরে আসে। কুইরিলিস্টে কৌনিক নিবন্ধ এবং একটি @ ভিউচিল্ড্রেন ব্যবহারের ডেমো দেখুন

@ ভিউচিল্ডারেন ফেরত পাওয়া ক্যোরিলিস্টটি ব্যবহার করতে পারেন এবং নীচে উল্লিখিত হিসাবে 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

আশা করি এটি কারও কিছু সময় এবং হতাশা বাঁচাতে সহায়তা করবে।


যে সমাধানটি আমার পক্ষে কাজ করেছিল তা হ'ল অ্যাপ.মডিউল.স.


আমার ক্ষেত্রে, আমি জানতাম যে সন্তানের উপাদানটি সর্বদা উপস্থিত থাকবে, তবে শিশুটি কাজ বাঁচানোর জন্য আরম্ভ করার আগে রাষ্ট্রটিকে পরিবর্তন করতে চেয়েছিল।

আমি সন্তানের উপস্থিত না হওয়া অবধি পরীক্ষা করার এবং অবিলম্বে পরিবর্তনগুলি বেছে নেওয়া বেছে নিয়েছি, যা আমাকে সন্তানের উপাদানগুলির একটি পরিবর্তন চক্রকে বাঁচায়।

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

সতর্কতা অবলম্বন করে আপনি সর্বাধিক সংখ্যক প্রচেষ্টা যুক্ত করতে পারেন বা setTimeout কয়েকটি এমএস বিলম্ব যোগ করতে setTimeoutsetTimeout কার্যকরভাবে মুলতুবি অপারেশনগুলির তালিকার নীচে ফাংশনটি ছুড়ে ফেলে।





angular