Обновить
0
0
Акулов Андрей@SlamJam

Фронтенд разработчик

Отправить сообщение

Отлично! Все разложено по полочкам.

Однако, есть вопрос зачем в примерах используется this.cdr.detectChanges() если в тексте указано, что данных механизм синхронный и markForCheck предпочтительнее? Как все-таки более правильно? Часто вижу, что в разных примерах используют detectChanges. Как по мне, так в 99% случаев нужен только markForCheck.

markForCheck только помечает компоненты как "грязные", а дальше работает уже стандартный цикл изменений ангуляра. detectChanges запускает цикл изменений вручную + он это делает для компонента и его потомков в дереве. Отсюда может быть просадка производительности. Вроде даже в доке было, что detectChanges нужно использовать для компонентов, которые отцепили от основного механизама обнаружения изменений.

В Firefox беда с линиями.

Вдохновляюсь каждый раз читая ваши статьи. И каждый раз удивляюсь насколько плохо, оказывается, я знаю Ангуляр, хотя работаю с ним каждый день.
В статье упомянут кейс с markForCheck, который повлияет только на родителя. А какой верный способ, чтобы обновить все компоненты по дереву view?

Теперь понятно. Упустил, что мы пайпим первый атрибут во втором. Спасибо за разъяснение!
Не совсем понял. А почему HostBinding для атрибута active создает подписки на поток атрибута test?

И тогда получается что создается не 4 пописки, а 8: по подписке на каждый HostBinding в каждом атрибуте.

Увеличивая количество атрибутов с подобным подходом, не получим ли проблем?
Спасибо!
Да, действительно так получается более обобщенное объявление. На первый взгляд больше boilerplate кода, но на выходе более гибко должно быть. Надо будет попробовать )
И еще вопрос. А в чем преимущество описания инъекции через декоратор Inject?
Так:
 constructor(
       @Inject(TuiDestroyService) destroy$: Observable<void>,
       @Inject(ChangeDetectorRef) changeDetectorRef: ChangeDetectorRef,
   ) { }

Вместо:
 constructor(
       destroy$: TuiDestroyService,
       changeDetectorRef: ChangeDetectorRef,
   ) { }
В последнем примере в для атрибута test в console.log вылетает по 4 одинаковых сообщения. Похоже это связано с количеством HostBinding. Но не понятно почему. EventManager слушает события на всех атрибутах?
Спасибо за очередную отличную статью! Команда Тинькофф Банка едва ли не единственный источник русскоязычных материалов по Angular.

А подскажите, в чем преимущество использования @Attribute вместо Input? Получение значения, которое не будет меняться по ходу работы приложения? Англяровская дока немногословна по этому поводу.
В примере ORGANIZATION_PROVIDERS — это массив провайдеров.
Далее в компоненте в массив providers просто добавляется ORGANIZATION_PROVIDERS. Без spread-оператора.
Это работает, т.к. providers видимо поддерживает работу с массивами.
Проблема вылезает, когда нужно сделать тест на такой компонент и переопределить провайдер: overrideComponent похоже не поддерживает массивы в providers. Со spread'ом работает.

Вопрос, собственно, следующий — не правильнее ли тогда везде использовать массив провайдеров со spread? Как вы решали подобные проблемы?
Ну если сделать прямой перевод на Rx, то получится как-то так:
fetchData = (itemId) => {
    this.isFetching = true
    of(apiRequest(`GET /item/${itemId}`).send()).pipe(
        map(item => {
            this.item = 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 reqUrl;
        }),
        switchMap(url => of(apiRequest(url).send())),
        finalize(() => this.isFetching = false)
    ).subscribe({
        next: (additionalInfo) => {
            this.additionalBlockInfo = additionalInfo;
        },
        error: (e) => {
            this.errorMessage = e.message;
        }
    })
}

Читаемости не сильно прибавилось. Но если уж брать 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)
    }
}


В этом случае код получается чище, богаче на различные обработки и защиты от дураков.
+ это все можно пилить на функции и компоновать в более сложные потоки.

Интересный прием, но в начале статьи ошибка: не !#, а #!..
А кто-нибудь из счастливых обладателей Ati x1200 уже обновился? Unity3D тормозит просто безбожно, приходится в 2D работать. В 11.04 все летало.
Кстати, были и хорошие интерфейсы. На Mac OS 7 тех же годов все выглядело очень симпатично. Недавно на древнем макинтоше, случайно попавшем к нам в офис, наблюдал.
Очень познавательный пост. Тема, прям, так развернуто раскрыта, что дух захватывает.
Очень интересная идея. А специалисты Google будут объяснять, что и зачем было сделано на каждом этапе?
Интересная статья, хоть и много букв.
«выборы, выборы, все кандидаты — ...» — это не шнуровское. Это творчество «Несчастного случая» совместно с Квартетом И. Это уже позднее в фильме эту песню Шнур исполнял.
Сильно сомневаюсь, что Миноразованию это надо. Нет, по началу денег даже дадут, но их распилят еще до того как они до вас дойдут. Потом интерес к вашему проекту угаснет и он тихонько скончается. Ну или будет влачить жалкое существование. Если есть возможность зарабатывать деньги самостоятельно и не ждать подачек сверху, то почему бы не попробовать?
* нестуденты. Вот, капец, я неграмотный. Лекции по русскому языку не помешают. Начиная с 7 класса.

Информация

В рейтинге
Не участвует
Откуда
Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Фронтенд разработчик, Фулстек разработчик
Ведущий
JavaScript
HTML
CSS
Angular
Vue.js
Python
Docker
Linux
Git
Английский язык