Вопрос
Какой самый элегантный способ получить @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()
метод, он становится видимым. Однако до того, как обнаружение изменений Angular 2 завершится, я не могу сослаться на viewContainerRef
. Я обычно оборачиваю все необходимые действия, setTimeout(()=>{},1)
как показано выше. Есть ли более правильный путь?
Я знаю, что есть опция с ngAfterViewChecked
, но она вызывает слишком много бесполезных звонков.
ОТВЕТ (Плункер)
angular
angular2-changedetection
sinedsem
источник
источник
Ответы:
Используйте сеттер для ViewChild:
Сеттер вызывается со ссылкой на элемент, как только
*ngIf
становитсяtrue
.Обратите внимание, что для Angular 8 вы должны обязательно установить
{ static: false }
, что является настройкой по умолчанию в других версиях Agnular:Примечание: если contentPlaceholder является компонентом, вы можете изменить ElementRef на класс компонента:
источник
contentPlaceholder
этоElementRef
не такViewContainerRef
.<div #contentPlaceholder></div>
. Технически вы можете вызвать это вручную, как любой другой сеттер,this.content = someElementRef
но я не понимаю, почему вы захотите это сделать.Альтернативой для преодоления этого является запуск детектора изменений вручную.
Вы сначала вводите
ChangeDetectorRef
:Затем вы вызываете его после обновления переменной, управляющей * ngIf
источник
onInit()
, поэтому я добавилdetectChanges
перед вызовом любую дочернюю функцию, и это исправило ее. (Я использовал как принятый ответ, так и этот ответ)Угловой 8+
Вы должны добавить
{ static: false }
в качестве второго варианта для@ViewChild
. Это приводит к тому, что результаты запроса разрешаются после выполнения обнаружения изменений, что позволяет@ViewChild
обновлять их после изменения значения.Пример:
Пример Stackblitz: https://stackblitz.com/edit/angular-d8ezsn
источник
<mat-horizontal-stepper *ngIf="viewMode === DialogModeEnum['New']" linear #stepper
,@ViewChild('stepper', {static: true}) private stepper: MatStepper;
иthis.changeDetector.detectChanges();
это все еще не работает.Ответы выше не сработали для меня, потому что в моем проекте ngIf находится на элементе ввода. Мне нужен был доступ к атрибуту nativeElement, чтобы сосредоточиться на вводе, когда ngIf равен true. Кажется, нет никакого атрибута nativeElement на ViewContainerRef. Вот что я сделал (следуя документации @ViewChild ):
Я использовал setTimeout перед фокусировкой, потому что ViewChild требуется секунда, чтобы быть назначенным. В противном случае это было бы неопределенным.
источник
setTimeout
?I usually wrap all required actions into setTimeout(()=>{},1) as shown above. Is there a more correct way?
Как упоминалось другими, самое быстрое и быстрое решение - использовать [скрытый] вместо * ngIf, так компонент будет создан, но не виден, так как вы можете иметь к нему доступ, хотя он может быть не самым эффективным путь.
источник
Это может сработать, но я не знаю, удобно ли это для вашего случая:
источник
ngAfterViewInit()
вместоngOnInit()
. Я предположил, чтоviewContainerRefs
он уже инициализирован, но еще не содержит элементов. Кажется, я запомнил это неправильно.AfterViewInit
на самом деле работает. Я удалил все свои комментарии, чтобы не запутать людей. Вот работающий Плункер: plnkr.co/edit/myu7qXonmpA2hxxU3SLB?p=previewconst
параметромViewChild
Другой быстрый «трюк» (простое решение) - просто использовать тег [hidden] вместо * ngIf, просто важно знать, что в этом случае Angular создает объект и рисует его в классе: hidden, поэтому ViewChild работает без проблем. , Поэтому важно помнить, что вы не должны использовать скрытые тяжелые или дорогие предметы, которые могут вызвать проблемы с производительностью
источник
Моя цель состояла в том, чтобы избежать любых хакерских методов, которые предполагают что-то (например, setTimeout), и я закончил тем, что реализовал принятое решение с небольшим количеством RxJS- аромата сверху:
Мой сценарий: я хотел запустить действие на
@ViewChild
элемента в зависимости от маршрутизатораqueryParams
. Из-за*ngIf
ложного переноса до тех пор, пока HTTP-запрос не вернет данные, инициализация@ViewChild
элемента происходит с задержкой.Как это работает:
combineLatest
выдает значение в первый раз, только когда каждый из предоставленных Observables выдает первое значение с моментаcombineLatest
подписки. Мой предметtabSetInitialized
испускает значение, когда@ViewChild
элемент устанавливается. При этом я откладываю выполнение кодаsubscribe
до*ngIf
положительного поворота и@ViewChild
инициализации.Конечно, не забудьте отменить подписку на ngOnDestroy, я делаю это, используя
ngUnsubscribe
тему:источник
Упрощенная версия, у меня была похожая проблема при использовании Google Maps JS SDK.
Моим решением было извлечь
div
иViewChild
в его собственный дочерний компонент, который при использовании в родительском компоненте можно было скрыть / отобразить с помощью*ngIf
.Перед
HomePageComponent
шаблонHomePageComponent
Составная частьПосле
MapComponent
шаблонMapComponent
Составная частьHomePageComponent
шаблонHomePageComponent
Составная частьисточник
В моем случае мне нужно было загружать весь модуль только тогда, когда в шаблоне присутствовал div, то есть выход находился внутри ngif. Таким образом, каждый раз, когда angular обнаруживает элемент #geolocalisationOutlet, он создает компонент внутри него. Модуль загружается только один раз.
источник
Я думаю, что использование defer от lodash имеет большой смысл, особенно в моем случае, когда мой
@ViewChild()
был внутриasync
трубыисточник
Работа на Angular 8 Нет необходимости импортировать ChangeDector
ngIf позволяет вам не загружать элемент и избегать дополнительной нагрузки на ваше приложение. Вот как я запустил его без ChangeDetector
Затем, когда я изменю свое значение ngIf на истинное, я буду использовать setTimeout, как этот, чтобы он ожидал только следующего цикла изменения:
Это также позволило мне избежать использования каких-либо дополнительных библиотек или импорта.
источник
для Angular 8 - смесь проверки нуля и
@ViewChild
static: false
взломадля пейджингового управления, ожидающего асинхронные данные
источник
Это работает для меня, если я использую ChangeDetectorRef в Angular 9
источник