Угловое 2 @ViewChild в * ngIf

Вопрос

Какой самый элегантный способ получить @ViewChild после отображения соответствующего элемента в шаблоне? Ниже приведен пример. Также доступен Plunker .

Шаблон:

Компонент:

 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) как показано выше. Есть ли более правильный путь?

Я знаю, что есть опция с ngAfterViewChecked , но она вызывает слишком много бесполезных вызовов.

ОТВЕТ (Плункер)

    Принятый ответ с использованием QueryList не работал для меня. Однако, что работа использовала сеттер для ViewChild:

      private contentPlaceholder: ElementRef; @ViewChild('contentPlaceholder') set content(content: ElementRef) { this.contentPlaceholder = content; } 

    Setter вызывается однажды * ngIf становится истинным.

    Альтернативой этому является запуск детектора изменений вручную.

    Сначала вы вводите ChangeDetectorRef :

     constructor(private changeDetector : ChangeDetectorRef) {} 

    Затем вы вызываете его после обновления переменной, которая управляет * ngIf

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

    Ответы выше не работали для меня, потому что в моем проекте ngIf находится на входном элементе. Мне нужен был доступ к атрибуту nativeElement, чтобы сфокусироваться на входе, когда ngIf истинно. Кажется, что нет атрибута nativeElement в ViewContainerRef. Вот что я сделал (после документации @ViewChild ):

      
    ... private assetInputElRef:ElementRef; @ViewChild('assetInput') set assetInput(elRef: ElementRef) { this.assetInputElRef = elRef; } ... showAsset() { this.showAssetInput = true; setTimeout(() => { this.assetInputElRef.nativeElement.focus(); }); }

    Я использовал setTimeout перед фокусировкой, потому что ViewChild занимает секунду. В противном случае это было бы неопределенным.

    Как упоминалось другим, самым быстрым и быстрым решением является использование [скрытый] вместо * ngIf, таким образом компонент будет создан, но не виден, там вы можете иметь доступ к нему, хотя он может быть не самым эффективным путь.

    Это может сработать, но я не знаю, удобно ли это для вашего дела:

     @ViewChildren('contentPlaceholder', {read: ViewContainerRef}) viewContainerRefs: QueryList; ngAfterViewInit() { this.viewContainerRefs.changes.subscribe(item => { if(this.viewContainerRefs.toArray().length) { // shown } }) } 

    Моя цель состояла в том, чтобы избежать хакерских методов, которые что-то предполагали (например, setTimeout), и я закончил реализацию принятого решения с немного аромата RxJS сверху:

      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 является ложным, пока HTTP-запрос не вернет данные, инициализация элемента @ViewChild происходит с задержкой.

    Как это работает: combineLatest испускает значение в первый раз только тогда, когда каждая из предоставленных Observables излучает первое значение с момента combineLatest . Моя тема tabSetInitialized испускает значение, когда @ViewChild элемент @ViewChild . При этом я задерживаю выполнение кода под subscribe до тех пор, пока *ngIf станет положительным и инициализируется @ViewChild .

    Конечно, не забудьте отказаться от подписки на ngOnDestroy, я делаю это с помощью ngUnsubscribe Subject:

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

    У меня была аналогичная проблема. Я исправил это, используя hidden :

    Вместо:

     

    Я использовал:

     

    *ngIf удаляет элемент из DOM, а [hidden] переключает отображение свойства CSS.

    Это работало для меня. Просто примените некоторую задержку после привязки данных к форме.

      setTimeout(() => { this.changeDetector.detectChanges(); }, 500); 
    Давайте будем гением компьютера.