@ ভিভিচিল্ড * এনজিআইএফ-তে


215

প্রশ্ন

@ViewChildটেমপ্লেটে সম্পর্কিত উপাদানটি দেখানোর পরে সর্বাধিক মার্জিত উপায় কী ?

নীচে একটি উদাহরণ দেওয়া আছে। এছাড়াও Plunker পাওয়া যায়।

টেমপ্লেট:

<div id="layout" *ngIf="display">
    <div #contentPlaceholder></div>
</div>

উপাদান:

export class AppComponent {

    display = false;
    @ViewChild('contentPlaceholder', {read: ViewContainerRef}) viewContainerRef;

    show() {
        this.display = true;
        console.log(this.viewContainerRef); // undefined
        setTimeout(()=> {
            console.log(this.viewContainerRef); // OK
        }, 1);
    }
}

ডিফল্টরূপে লুকিয়ে থাকা বিষয়বস্তুগুলির সাথে আমার একটি উপাদান রয়েছে। কেউ যখন show()পদ্ধতিটি কল করে তা দৃশ্যমান হয়। তবে, কৌণিক 2 পরিবর্তন সনাক্তকরণ সম্পূর্ণ হওয়ার আগে, আমি উল্লেখ করতে পারি না viewContainerRef। আমি সাধারণত সমস্ত প্রয়োজনীয় ক্রিয়া setTimeout(()=>{},1)উপরের মতো দেখান into আরও সঠিক উপায় আছে?

আমি জানি যে এর সাথে একটি বিকল্প রয়েছে ngAfterViewChecked, তবে এটি অত্যধিক অকেজো কলগুলির কারণ ঘটায়।

উত্তর (প্লাঙ্কার)


3
আপনি কি * এনজিআইএফ এর পরিবর্তে [লুকানো] অ্যাট্রিবিউটটি ব্যবহার করার চেষ্টা করেছিলেন? এটি একই পরিস্থিতির জন্য আমার পক্ষে কাজ করেছিল।
শারদুল

উত্তর:


335

ভিউচিল্ডের জন্য একটি সেটার ব্যবহার করুন:

 private contentPlaceholder: ElementRef;

 @ViewChild('contentPlaceholder') set content(content: ElementRef) {
    if(content) { // initially setter gets called with undefined
        this.contentPlaceholder = content;
    }
 }

সেটার নামক একটি উপাদান রেফারেন্স সহ একবার হয় *ngIfহয়ে true

দ্রষ্টব্য, কৌনিক 8 এর জন্য আপনাকে সেট করতে হবে তা নিশ্চিত করতে হবে { static: false }যা অন্যান্য অগ্নুলার সংস্করণগুলিতে একটি ডিফল্ট সেটিংস:

 @ViewChild('contentPlaceholder', { static: false })

দ্রষ্টব্য: কন্টেন্টপ্লেসহোল্ডার যদি একটি উপাদান হয় তবে আপনি এলিমেন্টরফকে আপনার উপাদান শ্রেণিতে পরিবর্তন করতে পারেন:

  private contentPlaceholder: MyCustomComponent;
  @ViewChild('contentPlaceholder') set content(content: MyCustomComponent) {
     if(content) { // initially setter gets called with undefined
          this.contentPlaceholder = content;
     }
  }

27
নোট করুন যে এই সেটারটিকে প্রাথমিকভাবে সংজ্ঞায়িত সামগ্রী দিয়ে ডাকা হয়, সুতরাং সেটে কিছু করা হলে নাল পরীক্ষা করুন
রেসিপ

1
ভাল উত্তর কিন্তু contentPlaceholderহয় ElementRefনা ViewContainerRef
বিকাশকারী 0

6
আপনি কিভাবে সেটটার কল?
লেয়ানড্রো কুস্যাক

2
কৌনিকটি খুঁজে পেলে লিন্ড্রো ক্যাস্যাকটি স্বয়ংক্রিয়ভাবে কল হয়ে যায় <div #contentPlaceholder></div>। প্রযুক্তিগতভাবে আপনি এটিকে অন্য যে কোনও সেটারের মতো ম্যানুয়ালি কল করতে পারেন this.content = someElementRefতবে আপনি কেন এটি করতে চান তা আমি দেখতে পাচ্ছি না।
সংসদ

3
যে কেউ এখন এই জুড়ে আসে তার জন্য কেবলমাত্র একটি সহায়ক নোট - আপনার @ ভিউচিল্ড ('মাই কম্পোম্পোনেন্ট', {স্ট্যাটিক: মিথ্যা}) থাকা দরকার যেখানে মূল বিটটি স্থিতিক: মিথ্যা, যা এটি বিভিন্ন ইনপুট নিতে দেয়।
nspamthanks

107

এটি কাটিয়ে উঠার একটি বিকল্প হ'ল ম্যানুয়ালি পরিবর্তন ডিটেক্টর চালাচ্ছে।

আপনি প্রথমে ইনজেক্ট করুন ChangeDetectorRef:

constructor(private changeDetector : ChangeDetectorRef) {}

তারপরে আপনি * ngIf নিয়ন্ত্রণ করে এমন ভেরিয়েবলটি আপডেট করার পরে এটি কল করবেন

show() {
        this.display = true;
        this.changeDetector.detectChanges();
    }

1
ধন্যবাদ! আমি স্বীকৃত উত্তরটি ব্যবহার করছি তবে এটি এখনও একটি ত্রুটি ঘটাচ্ছে কারণ বাচ্চারা এখনও তাদের অপরিবর্তিত ছিল যখন আমি তাদের কিছুক্ষণ পরে ব্যবহার করার চেষ্টা করেছি onInit(), তাই আমি detectChangesকোনও সন্তানের ক্রিয়াকলাপ কল করার আগে এটি যুক্ত করেছিলাম এবং এটি ঠিক করে দিয়েছে। (আমি গৃহীত উত্তর এবং এই উত্তর উভয়ই ব্যবহার করেছি)
Minyc510

সুপার সহায়ক! ধন্যবাদ!
অ্যাপড্রেমার

55

কৌণিক 8+

আপনার { static: false }জন্য দ্বিতীয় বিকল্প হিসাবে যুক্ত করা উচিত @ViewChild। পরিবর্তনের সনাক্তকরণটি চলার পরে ক্যোয়ারির ফলাফলগুলি সমাধান হওয়ার কারণ @ViewChildএটি মান পরিবর্তনের পরে আপনাকে আপডেট করার অনুমতি দেয় ।

উদাহরণ:

export class AppComponent {
    @ViewChild('contentPlaceholder', { static: false }) contentPlaceholder: ElementRef;

    display = false;

    constructor(private changeDetectorRef: ChangeDetectorRef) {
    }

    show() {
        this.display = true;
        this.changeDetectorRef.detectChanges(); // not required
        console.log(this.contentPlaceholder);
    }
}

স্ট্যাকব্লিটজ উদাহরণ: https://stackblitz.com/edit/angular-d8ezsn


3
ধন্যবাদ সিভিয়াটোস্লাভ। উপরের সমস্ত কিছুর চেষ্টা করেছেন কিন্তু কেবল আপনার সমাধান কাজ করেছে।
পিটার ড্রিনান

এটি আমার পক্ষেও কাজ করেছে (যেমন ভিউচিল্ড্রেন ট্রিকটি করেছে)। এই একটি কৌনিক 8 এর জন্য আরও স্বজ্ঞাত এবং সহজ
অ্যালেক্স

2
মনোমুগ্ধকর মতো কাজ করেছেন :)
সন্দীপ কে নায়ার

1
সর্বশেষ সংস্করণটির জন্য এটি গ্রহণযোগ্য উত্তর হওয়া উচিত।
কৃষ্ণ প্রশাট

আমি ব্যবহার করছি <mat-horizontal-stepper *ngIf="viewMode === DialogModeEnum['New']" linear #stepper, @ViewChild('stepper', {static: true}) private stepper: MatStepper;এবং this.changeDetector.detectChanges();এটি এখনও কার্যকর হয় না।
পল স্ট্রুপেইকিস

21

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

<button (click)='showAsset()'>Add Asset</button>
<div *ngIf='showAssetInput'>
    <input #assetInput />
</div>

...

private assetInputElRef:ElementRef;
@ViewChild('assetInput') set assetInput(elRef: ElementRef) {
    this.assetInputElRef = elRef;
}

...

showAsset() {
    this.showAssetInput = true;
    setTimeout(() => { this.assetInputElRef.nativeElement.focus(); });
}

আমি ফোকাস করার আগে সেটটাইমআউট ব্যবহার করেছি কারণ ভিউচিল্ড নির্ধারিত হতে একটি সেকেন্ড নেয়। অন্যথায় এটি অপরিজ্ঞাত হবে।


2
0 এর একটি সেটটাইমআউট () আমার জন্য কাজ করেছিল। আমার এনজিআইফ দ্বারা লুকানো আমার উপাদানটি সঠিকভাবে একটি সেটটাইমআউটের পরে আবদ্ধ ছিল, মাঝখানে সেট অ্যাসেটইনপুট () ফাংশনের প্রয়োজন ছাড়াই।
উইল শেভার

আপনি শোএসেটে পরিবর্তনগুলি সনাক্ত করতে পারেন () এবং সময়সীমা ব্যবহার করতে হবে না।
WrksOnMyMachine

এ কেমন উত্তর? ওপিতে ইতিমধ্যে একটি ব্যবহার করে উল্লেখ করা হয়েছে setTimeout? I usually wrap all required actions into setTimeout(()=>{},1) as shown above. Is there a more correct way?
হুয়ান মেন্ডেস

12

যেমনটি অন্যান্য দ্বারা উল্লেখ করা হয়েছিল, দ্রুত এবং দ্রুত সমাধানটি * এনজিআই এর পরিবর্তে [লুকানো] ব্যবহার করা হয়, এইভাবে উপাদানটি তৈরি করা হবে তবে দৃশ্যমান হবে না, তবে এতে আপনার অ্যাক্সেস থাকতে পারে, যদিও এটি সবচেয়ে কার্যকর নাও হতে পারে might উপায়।


1
আপনাকে লক্ষ্য রাখতে হবে যে উপাদানটি "প্রদর্শন: ব্লক" এর মধ্যে না থাকলে "[লুকানো]" ব্যবহার করা কাজ করতে পারে না। ভাল ব্যবহার [style.display] = "শর্ত '': 'কেউ'?"
ফেলিক্স Brunet

10

এটি কাজ করতে পারে তবে আমি জানি না এটি আপনার ক্ষেত্রে উপযুক্ত কিনা:

@ViewChildren('contentPlaceholder', {read: ViewContainerRef}) viewContainerRefs: QueryList;

ngAfterViewInit() {
 this.viewContainerRefs.changes.subscribe(item => {
   if(this.viewContainerRefs.toArray().length) {
     // shown
   }
 })
}

1
আপনি দয়া করে ngAfterViewInit()পরিবর্তে চেষ্টা করতে পারেন ngOnInit()। আমি ধরে নিয়েছি যে viewContainerRefsইতিমধ্যে সূচনা হয়েছে তবে এখনও আইটেমগুলিতে নেই। মনে হয় আমি এই ভুল মনে আছে।
Gternter Zöchbauer

দুঃখিত আমি ভূল ছিলাম. AfterViewInitআসলে কাজ করে। আমি মানুষকে বিভ্রান্ত না করার জন্য আমার সমস্ত মন্তব্য মুছে ফেলেছি। এখানে একটি কার্যনির্বাহী Plunker: plnkr.co/edit/myu7qXonmpA2hxxU3SLB?p= পূর্বরূপ
sendsem

1
এটি আসলে একটি ভাল উত্তর। এটি কাজ করে এবং আমি এটি এখন ব্যবহার করছি। ধন্যবাদ!
কনস্টান্টিন

1
এটি আমার জন্য কৌণিক 7 থেকে 8 পর্যন্ত আপগ্রেড হওয়ার পরে কাজ করেছে কোনও কারণে, আপগ্রেডটি স্থিতিশীল ব্যবহারের পরেও ভিউইনাইটে উপাদানটিকে অপরিজ্ঞাত করে তোলে: যখন উপাদানটি এনজিআইএফতে আবৃত ছিল তখন নতুন ভিউচিল্ড সিনট্যাক্স প্রতি মিথ্যা। আরও মনে রাখবেন যে ক্যোরিলিস্টে এখন এই ক্যোরিলিস্ট <টাইমকোম্পোনটি টাইপ> এর মতো টাইপ দরকার;
অ্যালেক্স

এর constপ্যারামিটারের সাথে সম্পর্কিত পরিবর্তন হতে পারেViewChild
গেঞ্জার জ্যাচবাউয়ার

9

আরেকটি দ্রুত "কৌতুক" (সহজ সমাধান) হ'ল * এনজিআইএফ এর পরিবর্তে [লুকানো] ট্যাগ ব্যবহার করা, এটি জেনে রাখা গুরুত্বপূর্ণ যে সেই ক্ষেত্রে কৌনিকটি বস্তুটি তৈরি করে এবং এটি শ্রেণীর আওতায় আঁকায়: লুকানো এই কারণেই ভিউচিল্ড কোনও সমস্যা ছাড়াই কাজ করে । সুতরাং এটি মনে রাখা গুরুত্বপূর্ণ যে আপনার ভারী বা ব্যয়বহুল আইটেমগুলিতে গোপন ব্যবহার করা উচিত নয় যা পারফরম্যান্স সমস্যার কারণ হতে পারে

  <div class="addTable" [hidden]="CONDITION">

যদি সেই
লুকানোটি

6

আমার লক্ষ্যটি এমন কোনও হ্যাকি পদ্ধতি এড়ানো ছিল যা কিছু ধরে নেয় (উদাঃ সেটটাইমআউট) এবং আমি উপরের কিছুটা আরএক্সজেএস স্বাদ গ্রহণযোগ্য সমাধানটি প্রয়োগ করে শেষ করেছি :

  private ngUnsubscribe = new Subject();
  private tabSetInitialized = new Subject();
  public tabSet: TabsetComponent;
  @ViewChild('tabSet') set setTabSet(tabset: TabsetComponent) {
    if (!!tabSet) {
      this.tabSet = tabSet;
      this.tabSetInitialized.next();
    }
  }

  ngOnInit() {
    combineLatest(
      this.route.queryParams,
      this.tabSetInitialized
    ).pipe(
      takeUntil(this.ngUnsubscribe)
    ).subscribe(([queryParams, isTabSetInitialized]) => {
      let tab = [undefined, 'translate', 'versions'].indexOf(queryParams['view']);
      this.tabSet.tabs[tab > -1 ? tab : 0].active = true;
    });
  }

আমার পরিস্থিতি: আমি @ViewChildরাউটারের উপর নির্ভর করে কোনও উপাদানের উপর কোনও ক্রিয়াকলাপ শুরু করতে চেয়েছিলাম queryParams*ngIfএইচটিটিপি অনুরোধ ডেটা ফেরত না দেওয়া পর্যন্ত কোনও মোড়ক মিথ্যা হওয়ার কারণে , @ViewChildউপাদানটির সূচনা বিলম্বের সাথে ঘটে।

এটি কীভাবে কাজ করে: combineLatest কেবলমাত্র প্রদত্ত পর্যবেক্ষণযোগ্য প্রতিটি মুহুর্তের combineLatestসাবস্ক্রাইব হওয়ার পরে প্রথম মানটি নির্গত হয় তখনই প্রথমবারের জন্য একটি মান নির্গত হয়। tabSetInitializedযখন @ViewChildউপাদানটি সেট করা হচ্ছে তখন আমার সাবজেক্ট একটি মান প্রেরণ করে। এর মাধ্যমে, আমি কোডটির প্রয়োগের ক্ষেত্রে বিলম্ব করি subscribeযতক্ষণ না *ngIfবাঁকগুলি ইতিবাচক হয় এবং @ViewChildপ্রারম্ভিক না হয়ে যায় ।

অবশ্যই এনজিওডেস্ট্রয় সাবস্ক্রাইব করতে ভুলবেন না, আমি ngUnsubscribeসাবজেক্টটি ব্যবহার করে এটি করি :

  ngOnDestroy() {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

অনেক ধন্যবাদ ধন্যবাদ আমার একই সমস্যা হয়েছে, ট্যাবসেট ও এনজিআইএফ সহ, আপনার পদ্ধতিটি আমাকে অনেক সময় এবং মাথা ব্যথা বাঁচিয়েছে। চিয়ার্স এম 8;)
প্রস্থান করুন 0

3

সরলিকৃত সংস্করণ, গুগল ম্যাপস জেএস এসডিকে ব্যবহার করার সময় আমার এই একই সমস্যা ছিল।

আমার সমাধান বের করে আনতে ছিল divএবং ViewChild/ একটি ব্যবহার প্রদর্শিত সেটিকে নিজের সন্তান উপাদান যখন পিতা বা মাতা উপাদান ব্যবহার করা HID হতে সক্ষম ছিল *ngIf

আগে

HomePageComponent টেমপ্লেট

<div *ngIf="showMap">
  <div #map id="map" class="map-container"></div>
</div>

HomePageComponent উপাদান

@ViewChild('map') public mapElement: ElementRef; 

public ionViewDidLoad() {
    this.loadMap();
});

private loadMap() {

  const latLng = new google.maps.LatLng(-1234, 4567);
  const mapOptions = {
    center: latLng,
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
  };
   this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
}

public toggleMap() {
  this.showMap = !this.showMap;
 }

পরে

MapComponent টেমপ্লেট

 <div>
  <div #map id="map" class="map-container"></div>
</div>

MapComponent উপাদান

@ViewChild('map') public mapElement: ElementRef; 

public ngOnInit() {
    this.loadMap();
});

private loadMap() {

  const latLng = new google.maps.LatLng(-1234, 4567);
  const mapOptions = {
    center: latLng,
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
  };
   this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
}

HomePageComponent টেমপ্লেট

<map *ngIf="showMap"></map>

HomePageComponent উপাদান

public toggleMap() {
  this.showMap = !this.showMap;
 }

1

আমার ক্ষেত্রে আমার কেবলমাত্র পুরো মডিউলটি লোড করা দরকার যখন টেমপ্লেটে ডিভ বিদ্যমান ছিল, যার অর্থ আউটলেটটি কোনও এনজিআইফের অভ্যন্তরে ছিল। এভাবে প্রতিটি সময়ে কৌণিক #geolocalisationOutlet উপাদানটি সনাক্ত করে এটি এর ভিতরে উপাদানটি তৈরি করে। মডিউলটি কেবল একবারে লোড করে।

constructor(
    public wlService: WhitelabelService,
    public lmService: LeftMenuService,
    private loader: NgModuleFactoryLoader,
    private injector: Injector
) {
}

@ViewChild('geolocalisationOutlet', {read: ViewContainerRef}) set geolocalisation(geolocalisationOutlet: ViewContainerRef) {
    const path = 'src/app/components/engine/sections/geolocalisation/geolocalisation.module#GeolocalisationModule';
    this.loader.load(path).then((moduleFactory: NgModuleFactory<any>) => {
        const moduleRef = moduleFactory.create(this.injector);
        const compFactory = moduleRef.componentFactoryResolver
            .resolveComponentFactory(GeolocalisationComponent);
        if (geolocalisationOutlet && geolocalisationOutlet.length === 0) {
            geolocalisationOutlet.createComponent(compFactory);
        }
    });
}

<div *ngIf="section === 'geolocalisation'" id="geolocalisation">
     <div #geolocalisationOutlet></div>
</div>


0

কৌণিক 8 এ কাজ করা চেঞ্জডেক্টর আমদানি করার দরকার নেই

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

elem: ElementRef;

@ViewChild('elemOnHTML', {static: false}) set elemOnHTML(elemOnHTML: ElementRef) {
    if (!!elemOnHTML) {
      this.elem = elemOnHTML;
    }
}

তারপরে আমি যখন আমার এনজিআইএফ মানটি সত্য হতে পরিবর্তন করি আমি কেবল পরবর্তী পরিবর্তন চক্রের জন্য অপেক্ষা করার জন্য এই জাতীয় সেটটাইমআউট ব্যবহার করব:

  this.showElem = true;
  console.log(this.elem); // undefined here
  setTimeout(() => {
    console.log(this.elem); // back here through ViewChild set
    this.elem.do();
  });

এটি আমাকে কোনও অতিরিক্ত লাইব্রেরি বা আমদানি ব্যবহার এড়াতে অনুমতি দেয়।


0

জন্য কৌণিক 8 নাল পরীক্ষণ এবং মিশ্রণ - @ViewChild static: falseগোরুর গাড়ি

অ্যাসিঙ্ক ডেটার জন্য অপেক্ষা করা একটি পেজিং নিয়ন্ত্রণের জন্য

@ViewChild(MatPaginator, { static: false }) set paginator(paginator: MatPaginator) {
  if(!paginator) return;
  paginator.page.pipe(untilDestroyed(this)).subscribe(pageEvent => {
    const updated: TSearchRequest = {
      pageRef: pageEvent.pageIndex,
      pageSize: pageEvent.pageSize
    } as any;
    this.dataGridStateService.alterSearchRequest(updated);
  });
}

0

আমি কৌনিক 9 এ চেঞ্জডেেক্টরআরফ ব্যবহার করি তবে এটি আমার পক্ষে কাজ করে

@ViewChild('search', {static: false})
public searchElementRef: ElementRef;

constructor(private changeDetector: ChangeDetectorRef) {}

//then call this when this.display = true;
show() {
   this.display = true;
   this.changeDetector.detectChanges();
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.