Pull to refresh

Comments 26

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

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

Можете рассказать, сколько потребуется правок, чтобы добавить в разметку блок, который будет отображаться в момент загрузки данных?

Прочитав статью (заметку?), я так и не понял, что хотел донести до нас автор: что у него не реализовано отслеживание статуса загрузки, или то, что новая библиотека поможет с решением того, что можно решить без это библиотеки?

Всегда в таких случаях возникает вопрос: зачем тянуть ещё библиотеки, если половину из описываемого можно решить без них?

Пользуюсь активно Rx (для JS и C#) - каких-либо проблем не наблюдаю с загрузкой данных... То есть, я могу сказать, что проблема искусственная(?)..

Это будет в следующей статье, и там для этого будет и новый контейнер, и другой компонент. Текст постараюсь сегодня подправить, чтобы переход был понятен.

Вроде в туториалах ангуляра вот такой подход используется. Чем он не устроил?

	<ng-template [ngIf]="profile$ | async as profile; else profileLoadingOrError">
		{{profile}}
	</ng-template>
	<ng-template #profileLoadingOrError>
		<ng-template [ngIf]="profileError$ | async as error; else profileLoading">
			{{error}}
		</ng-template>
	</ng-template>
	<ng-template #profileLoading>
		Loading...
	</ng-template>
		profile$ = ...; // .pipe(shareReplay(1))
		profileError$ = profile$.pipe(catchError(error => of(error)));

Покрывает все три состояния лоадинг/ошибка/результат и лишнего бойлерплейта почти нет.

Всё это конечно можно попробоывать завернуть в одну структурную директиву и вообще будет красиво.

Этот подход и есть пример из статьи, который мне не нравится.

  1. Он громоздкий как в темплейте, так и в коде

  2. Его придется вручную проверять в случае, когда на основе первых данных придется делать вызовы для загрузки следующих - if (error) else

  3. Придется постоянно держать в уме все случаи, когда надо менять флажки - тот же isLoading не может быть true в случае когда данные загружены, а модель это обычно позволяет.

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

В том же Ангуляре можно писать вообще так:

<div *showWaitingIndicatorAndErrorMessageAutomatically>
  <div>Name: {{name}}</div>
  <div>Age: {{age}}</div>
</div>
get data() { return fetchJSON( '/profile' ) }
get name() { return this.data.name }
get age() { return currentYear - this.data.birth.year }

Почему все так не делают, для меня остаётся загадкой. Возможно дело в стокгольмском синдроме из-за RxJS.

А как будет выглядеть код showWaitingIndicatorAndErrorMessageAutomatically хотя бы примерно?

Это оптимизированная версия ngIf + AsyncPipe, для обычных обcерваблов не подойдет. rxLet из rx-angular решает почти все вопросы из этой статьи и сомневаюсь, что можно сделать что-то лучше на данный момент.

Оно к RX вообще никакого отношения не имеет.

Да, разумеется, это специфчное для mobx-angular.

Да, Reactive context | RxAngular (rx-angular.io) - решает последствия проблемы (показ ошибки), но не причину (падение сети и подписок), хотя с ней будет попроще. Мое решение будет в следующей статье

Почему же это так нравится тем, кто это уже изучил?

Я изучил, но мне не нравится. Что со мной не так?

Потому что вы начинаете понимать все могуществао RxJS, когда парой операторов вы можете сделать то, что в императивном коде писали бы полдня на двух страницах.

const FilterSource = new BehaviorSubject< null | ( toy: Toy )=> boolean >( null )
const Filter = FilterSource.pipe( distinctUntilChanged() , debounce(0) , shareReplay(1) )

const ToysSource = new BehaviorSubject( [] )
const Toys = ToysSource.pipe( distinctUntilChanged() , debounce(0) , shareReplay(1) )

const ToysFiltered = Filter.pipe(
	switchMap( filter => {
		if( !filter ) return Toys
		return Toys.pipe( map( toys => toys.filter( filter ) ) )
	} ) ,
	distinctUntilChanged() ,
	debounce(0) ,
	shareReplay(1) ,
)
class Toys {

	@mem filter( next = null as null | ( toy: Toy )=> boolean ) { return next }
	
	@mem toys( next = [] ){ return next }
	
	@mem toysFiltered() {
		if( !this.filter() ) return this.toys()
		return this.toys().filter( this.filter() )
	}
	
}

в случае первых красиво падать, в случае вторых - уметь с ними работать и показывать нашим пользователям что-то

А можно в обоих случаях не падать, а продолжать корректную работу?

Код, соответственно, выглядит вот так:

Шёл 2022 год, шаблоны всё ещё писали без подсветки синтаксиса. И выкладывали в виде скриншотов.

Я изучил, но мне не нравится. Что со мной не так?

Полагаю то, что вы эксперт mol, а не Angular.

Шёл 2022 год, шаблоны всё ещё писали без подсветки синтаксиса. И выкладывали в виде скриншотов.

Подсветка везде есть, насколько вижу, да и код у меня через редактор хабра получался больше по размеру, поэтому скриншоты.

Ссылки на рабочий код, который можно скопировать, также везде присутствуют.

Одно другое не исключает.

80% кода зелёным цветом.

Полагаю имелся в виду eitherify оператор? Потому что сама структура данных отличается фактически всем :)

materialize вообще используется крайне редко, но вот например семпл в чем его отличие от eitherify - вместо 5 next + 1 complete будет значительно больше. Основное отличие в документации - When the source Observable emits error, the output will emit next as a Notification of type "error", and then complete. То есть на каждую ошибку вы будете опять делать complete вместо продолжения потока. Кроме того, ответ будет изначально одинаковый для разных HttpErrorCode, в то время как eitherify каждый может подправить под себя исходя из того, какой код пришел - 400, 401, 403, 500

Ссылка на stackblitz

Вы каждое значение в диапазоне оборачиваете в отдельный поток, поэтому у вас получается 5 complete. Но зачем?! В обычном потоке complete прилетает один раз в конце по завершению потока. В остальном же поведение eitherify и materialize как по мне одинаковые. Ну а получать на выходе union тип или же notification это уже вкусовщина.

Потому что Ангуляр с HttpClient тоже вызывает complete - вот демо. И materialize вызывает два раза next, что совсем не то что нужно при подписке.

Не Ангуляр с HttpClient вызывает дополнительный complete, это вы его вызываете подписавшись через объект с complete обработчиком. Вот вам альтернативный пример без дополнительного вызова complete stackblitz.com

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

Первая строка в случае получения данных, вторая - закрытие успешного потока, третья - ошибка. В этом нет complete после каждого next-а, как в вашем первом примере и нет дубликации complete как в вашем втором примере. Откройте в конце концов документацию и прочитайте её внимательно.

  1. Откройте ваше демо

  2. Нажмите Run OK - то что должно успешно загрузить пользователя. Подписчик ожидает получить в случае успеха 1 запись с профилем, но так как ангуляр вызывает комплит для хттп вызова, а materialize сконвертирует комплит в ещё один next, то подписчик получит 2 вызова next в сабскрайбе

  3. Документацию я конечно же прочитал

    RxJS: When the source Observable emits complete, the output Observable will emit next as a Notification of type "complete", and then it will emit complete as well. 

    Angular: The full body has been received and delivered, no further events are possible. This request is complete.

  4. Я не имею в виду, что materialize дублирует любой next источника. Я говорю о том что в связке с хттп клиентом Ангуляра он будет делать дубликаты.

  5. Если вы все еще не понимаете почему materialize вызовет дважды next у подписчика Angular HttpClient то, наверное, действительно стоит закончить дискуссию.

Обожаю эти вечные срачи о том как же именно работает тот или иной оператор. Думаю в этом мире нет ни одного человека, который понимает работу их всех.

От родного ангуляровского HttpClient отказался именно из-за убогой реализации ошибок, когда толком непонятно что за ошибка из него вылетает, когда он сам решает какой респонс код считать ошибкой, когда вынуждает писать мутные catchError и тп.

В целом, я считаю, что ангуляр и rxjs-way это адовая смесь: будет сложно, будут десятки операторов, будет совершенно непрозрачная логика, будут бесполезные стектрейсы, будут корявые до ужаса реактивные формы, будет куча асинкпайпов в шаблоне, будут лишние структурные директивы и темплейт-переменные.

И как бы в целом да, правильное направление, но до чего оно все квадратное...

Sign up to leave a comment.

Articles