Однако, есть вопрос зачем в примерах используется this.cdr.detectChanges() если в тексте указано, что данных механизм синхронный и markForCheck предпочтительнее? Как все-таки более правильно? Часто вижу, что в разных примерах используют detectChanges. Как по мне, так в 99% случаев нужен только markForCheck.
markForCheck только помечает компоненты как "грязные", а дальше работает уже стандартный цикл изменений ангуляра. detectChanges запускает цикл изменений вручную + он это делает для компонента и его потомков в дереве. Отсюда может быть просадка производительности. Вроде даже в доке было, что detectChanges нужно использовать для компонентов, которые отцепили от основного механизама обнаружения изменений.
Вдохновляюсь каждый раз читая ваши статьи. И каждый раз удивляюсь насколько плохо, оказывается, я знаю Ангуляр, хотя работаю с ним каждый день. В статье упомянут кейс с markForCheck, который повлияет только на родителя. А какой верный способ, чтобы обновить все компоненты по дереву view?
Спасибо!
Да, действительно так получается более обобщенное объявление. На первый взгляд больше boilerplate кода, но на выходе более гибко должно быть. Надо будет попробовать )
В последнем примере в для атрибута test в console.log вылетает по 4 одинаковых сообщения. Похоже это связано с количеством HostBinding. Но не понятно почему. EventManager слушает события на всех атрибутах?
Спасибо за очередную отличную статью! Команда Тинькофф Банка едва ли не единственный источник русскоязычных материалов по Angular.
А подскажите, в чем преимущество использования @Attribute вместо Input? Получение значения, которое не будет меняться по ходу работы приложения? Англяровская дока немногословна по этому поводу.
В примере ORGANIZATION_PROVIDERS — это массив провайдеров.
Далее в компоненте в массив providers просто добавляется ORGANIZATION_PROVIDERS. Без spread-оператора.
Это работает, т.к. providers видимо поддерживает работу с массивами.
Проблема вылезает, когда нужно сделать тест на такой компонент и переопределить провайдер: overrideComponent похоже не поддерживает массивы в providers. Со spread'ом работает.
Вопрос, собственно, следующий — не правильнее ли тогда везде использовать массив провайдеров со spread? Как вы решали подобные проблемы?
Читаемости не сильно прибавилось. Но если уж брать Rx, то и подход лучше поменять на реактивный.
Порассуждаем о задаче: необходимо выбирать данные о некотором элементе.
Допустим, что элементов у нас может быть несколько и пользователь их может быстро прокликивать, а отобразить данные о элементе нужно только в том случае, если получен весь набор.
Тогда появляются следующие потребности:
Собирать всю информацию об элементе и не рисовать (не присваивать в атрибуты объекта) ничего до полного получения инфы
Не нужно никуда присваивать данные если пришел новый запрос на получение элемента
Не нужно делать запросы на сервер на каждый клик пользователя — он может делать это очень быстро. Просто по приколу
Ну и хотелось бы иметь единое место, которое отвечает за получение информации
Если принять эти условия, то код на промисах, даже с async/await разрастется.
На Rx это можно решить как-то так:
class SomeClass {
constructor() {
// Создаем поток, для передачи информации об элементе, который нужно получить
this.needToFetchData$ = new Subject();
// Создадим подписку на поток, в которой будет реализована вся логика
this.needToFetchData$.pipe(
// Запретим выбирать данные об элементе, если на него только что уже кликали
skipUntilChanged(),
// Запретим слишком часто кликать на элементы
debounceTime(300),
// При получении нового id элемента, выставим индикатор загрузки
tap(() => this.isFetching = true),
// выполним запрос на сервер
// Важное замечание оператор switchMap переключает поток на новый при получении данных из родительского потока.
// Если уже было переключение и оно не завершилось, то оно отменяется. Цепочка дальше не пойдет.
switchMap((itemId) => of(apiRequest(`GET /item/${itemId}`).send())),
// Обработаем полученный элемент, чтобы понять как получать данные дальше
map(item => {
let reqUrl;
if (item.type === 'some_type1') {
reqUrl = `GET /some/url1`;
}
else if (item.type === 'some_type2') {
reqUrl = `GET /some/url2`;
} else {
reqUrl = `GET /some/url3`;
}
// Вернем собранный объект из элемента и урла, на который отправляем запрос
return { item, reqUrl };
}),
// Снова переключаем поток, чтобы получить дополнительную информацию
switchMap(itemData => of(apiRequest(itemData.reqUrl).send())
.pipe(
// А тут скомпонуем изначальный элемент и дополнительную информацию
map(additionalInfo => {
return { item: itemData.item, additionalInfo: additionalInfo };
})
)
),
// Выключим индикатор загрузки
finalize(() => this.isFetching = false)
).subscribe({
next: (gotData) => {
// присвоим полученные данные в атрибуты.
// До этого места дойдет только последний кликнутый пользователем элемент.
this.item = gotData.item;
this.additionalBlockInfo = gotData.additionalInfo;
},
error: (e) => {
// Выведем ошибку
this.errorMessage = e.message;
}
})
}
fetchData(itemId) {
// Инициируем получение данных об элементе по его id
this.needToFetchData$.next(itemId)
}
}
В этом случае код получается чище, богаче на различные обработки и защиты от дураков.
+ это все можно пилить на функции и компоновать в более сложные потоки.
Кстати, были и хорошие интерфейсы. На Mac OS 7 тех же годов все выглядело очень симпатично. Недавно на древнем макинтоше, случайно попавшем к нам в офис, наблюдал.
«выборы, выборы, все кандидаты — ...» — это не шнуровское. Это творчество «Несчастного случая» совместно с Квартетом И. Это уже позднее в фильме эту песню Шнур исполнял.
Сильно сомневаюсь, что Миноразованию это надо. Нет, по началу денег даже дадут, но их распилят еще до того как они до вас дойдут. Потом интерес к вашему проекту угаснет и он тихонько скончается. Ну или будет влачить жалкое существование. Если есть возможность зарабатывать деньги самостоятельно и не ждать подачек сверху, то почему бы не попробовать?
Отлично! Все разложено по полочкам.
Однако, есть вопрос зачем в примерах используется
this.cdr.detectChanges()если в тексте указано, что данных механизм синхронный иmarkForCheckпредпочтительнее? Как все-таки более правильно? Часто вижу, что в разных примерах используют detectChanges. Как по мне, так в 99% случаев нужен только markForCheck.markForCheck только помечает компоненты как "грязные", а дальше работает уже стандартный цикл изменений ангуляра. detectChanges запускает цикл изменений вручную + он это делает для компонента и его потомков в дереве. Отсюда может быть просадка производительности. Вроде даже в доке было, что detectChanges нужно использовать для компонентов, которые отцепили от основного механизама обнаружения изменений.
В Firefox беда с линиями.
Вдохновляюсь каждый раз читая ваши статьи. И каждый раз удивляюсь насколько плохо, оказывается, я знаю Ангуляр, хотя работаю с ним каждый день.
В статье упомянут кейс с
markForCheck, который повлияет только на родителя. А какой верный способ, чтобы обновить все компоненты по дереву view?И тогда получается что создается не 4 пописки, а 8: по подписке на каждый HostBinding в каждом атрибуте.
Увеличивая количество атрибутов с подобным подходом, не получим ли проблем?
Да, действительно так получается более обобщенное объявление. На первый взгляд больше boilerplate кода, но на выходе более гибко должно быть. Надо будет попробовать )
Так:
Вместо:
А подскажите, в чем преимущество использования @Attribute вместо Input? Получение значения, которое не будет меняться по ходу работы приложения? Англяровская дока немногословна по этому поводу.
Далее в компоненте в массив providers просто добавляется ORGANIZATION_PROVIDERS. Без spread-оператора.
Это работает, т.к. providers видимо поддерживает работу с массивами.
Проблема вылезает, когда нужно сделать тест на такой компонент и переопределить провайдер: overrideComponent похоже не поддерживает массивы в providers. Со spread'ом работает.
Вопрос, собственно, следующий — не правильнее ли тогда везде использовать массив провайдеров со spread? Как вы решали подобные проблемы?
Читаемости не сильно прибавилось. Но если уж брать Rx, то и подход лучше поменять на реактивный.
Порассуждаем о задаче: необходимо выбирать данные о некотором элементе.
Допустим, что элементов у нас может быть несколько и пользователь их может быстро прокликивать, а отобразить данные о элементе нужно только в том случае, если получен весь набор.
Тогда появляются следующие потребности:
Если принять эти условия, то код на промисах, даже с async/await разрастется.
На Rx это можно решить как-то так:
В этом случае код получается чище, богаче на различные обработки и защиты от дураков.
+ это все можно пилить на функции и компоновать в более сложные потоки.