Как стать автором
Обновить

Комментарии 9

В чем смысл директивы elementRef? Можно же просто обращаться к якорю напрямую:
<img #element>
<button (click)="element.focus()"></button>
Если у тебя нативный элемент то да, но если у тебя там компонент, то template reference variable будет ссылаться на его инстанс, а не на его DOM элемент.
Прошу прощения, не внимательно проскринил статью, приём корректный.
Если у вас нет возможности полностью перейти на OnPush, можно провести оптимизации.

А есть какой-то глобальный метод для полного перехода на OnPush? Например отказ от zonejs?

OnPush всё равно работает с зоной, от неё нет нужды отказываться. Нужно для начала избавиться от всех мутаций данных. А там посмотреть, если где-то не подцепляются изменения — нужно капнуть почему. Чаще всего там что-то изменяется в асинхронных коллбэках. Можно пройтись по всем subscribe, setTimeout, setInterval и тому подобному и глянуть, можно ли там перейти на async пайп в шаблоне. Если нет, то надо заинжектить ChangeDetectorRef и сделать на нём markForCheck().

OnPush под капотом юзает NgZone, убрав зоны ничего детектиться не будет. Без зон жить можно (и это актуально для Angular Elements), но вся ответственность на CD будет лежать на вас, придется инжектить CDR и вручную вызывать его методы, но можно использовать такие штуки вроде пайпа ngrxPush, аля AsyncPipe работающий без зон, еще есть ngrxLet, аналог ngFor. @ngrx/component снимает с вас значительную часть нагрузки по управлению ручным манипулированием CD (беззональное приложение), но пакет еще находится в стадии тестирования.

Просто пишите код так, как задумано принципами фреймворка, и вы не попадете в неприятности с OnPush

Ровно до тех пор не заходите уведомить другие дочерние компоненты родительского компонента о том что "надо бы перерендерится". Проблема в том что при onpush компонент обновиться только если изменятся @Input() (см ниже) или если он сам вызовет markForCheck(), способа сказать angular-у "перерендерь меня полностью со всеми подкомпонентами" до сих пор не существует :( Самый простой и очевидный способ решить проблему через @Input(), но, как указано в статье, надо помнить о том что нельзя просто взять изменить свойство, надо создавать новый объект, в некоторых случаях это неудобно.


Кроме этого в статье упущен еще один важный момент: если вы изменяете @Input() программно, вы обязаны вызвать markForCheck()иначе angular не поймет что надо обновить компонент. На практике это значит что если вы хотите чтобы ваш компонент работал во всех случаях, то в каждый сеттер каждого @Input() надо пихать проверку действительно ли значение изменилось и вызывать markForCheck() когда это произошло (ну или проверять в ngDoCheck()).

Именно об этом я и говорю. По принципам фреймворка данные движутся сверху вниз, события снизу вверх. Если вам нужно перерендерить дочерние компоненты, а при этом их входные данные не поменялись — вы делаете что-то не по канонам Angular. У самого был подобный кейс. Поведением внутреннего компонента управляла расположенная выше по дереву директива, чтобы избежать пробрасывания инпутов. Вложенный компонент брал её из DI. Решил я эту задачу добавлением стрима:
readonly refresh$: Observable<void> = new Subject();
Директива делала next в него в ngOnChanges, а нижестоящий компонент запускал по этому стриму проверку изменений. Возможно вам такой паттерн тоже подойдёт.


Ну а программное изменение инпутов компонента — это проделки Императора, смотрите совет №5 :)

Если вам нужно перерендерить дочерние компоненты, а при этом их входные данные не поменялись — вы делаете что-то не по канонам Angular

Это обычный очень распространенный кейс:


<страница которая с сервера объект с дочерними элементам>
    <заголовок, который используется на многих других страницах и вычисляет статус по дочерними элементам />

    <дочерний элемент №1>
        <редактировать />
        <удалить />
    </дочерний элемент №1>

    <дочерний элемент №2>
        <редактировать />
        <удалить />
    </дочерний элемент №2>
</страница которая с сервера объект с дочерними элементам>

Когда дочерний элемент удаляет/изменят сам себя надо
1) уведомить страницу
2) перерисовать всё компоненты


Когда объект действительно сложный гораздо проще было бы сказать "перерендерь меня полностью", но вместо этого приходится создавать глубокую копию*, ну или пробрасывать refresh$, но это как раз (очередной) костыль для решения проблемы, которую должен решать фреймфорк. Более того, refresh$, в отличии от клона, может не работать когда где-то внутри есть сторонние компоненты, который о нем не знают.


* — еще можно реализовать deep check (обычно оверкил), или фейковый @Input (ничем не лучше refresh$).


Ну а программное изменение инпутов компонента — это проделки Императора, смотрите совет №5 :)

Далеко не все можно написать декларативно. Ну а если вы не учитываете что ваш компонент может быть использовать программно, то вы сами роете себе яму чтобы попасть в неё в будущем.


См: https://github.com/angular/angular/issues/22567

Зарегистрируйтесь на Хабре , чтобы оставить комментарий