rx-angular / rx-angular

Reactive Extensions for Angular.
https://www.rx-angular.io/
MIT License
1.89k stars 193 forks source link

Context - higher order component #450

Open BioPhoton opened 3 years ago

BioPhoton commented 3 years ago

Context

Higher Oerder Component


context-teaser context-example rx-context-demo-hladky-michael-rx-angular

Case Study

The subject is a view that displays data e.g. a list of items or one single piece of information. In this case, the template structure consists of a loading spinner a search box, and a list.

The situation explained in steps:

Prior Ar

@Component({
  selector: 'any-component',
  template: `
    <p *ngIf="list$ | async as list; else loadingOrError">
        List count: {{ list.length}}
    </p>

    <ng-template #loadingOrError>
        <p *ngIf="isError$ | async; else loading">Error!</p>
        <ng-template #loading>Loading...</ng-template>
    </ng-template>
  `
})
export class AnyComponent  {
  list$ = this.globalState.list$;
  isError$ = new BehaviorSubject<boolean>(false);
}

Problem Solved By The Feature

When we want to create a simple type-ahead search it requires quite some knowledge and a lot of template structures to set this up.

In the above example we handle 2 different states:

Template Management

Consider the above case with the loading template and the list, it would get displayed and hidden several times in the user's interaction. In such situations, the performance suffers from instantiating those components multiple times in the if/else selections.

Mental-model and cognitive load

The examples code and several related structures are hard to implement and understand. The primary goal of handling user feedback and it's contextual information is not well defined either in state-management, component level, or directives.

This often leads to inconsistent implementations and mental models.

Hard to integrate

Even with helper components the, current concepts of integrating user feedback are poorly respected in today's solutions. It's hard to structure template wise, and there is no way of abstracting those states as meta-level info wrapped around any situation where user feedback is needed.

Hard to extend

The common approaches to context-related information often focus on only a part of the possible states or unite some of them as seen above. This makes it hard to handle the different states in the template or the component class.

Solution

The solution serves to layers which will improve the situation. It will structure and categorize the context-related information as well as enable us to switch in between those states based on the source of data itself, or user-controlled triggers.

@Component({
 selector: 'any-component',
 template: `
 <ul [context]=”list$; suspense: suspense$; error: error$; complete: complete$;”>
   <li *rxFor=”let item of list$”>{{item}}</li>
   <!-- Templates     👇 -->
   <li rxSuspense>Loading…</li>
   <li rxError>Ups…</div>
   <li rxComplete>Tadaa!</li>
 </ul>
 `
})
export class Component {
list$ = this.globalState.list$;

suspense$ = new Subject<boolean>(false);
error$ = new Subject<boolean>(false);
complete$ = new Subject<boolean>(false);
}

Concepts

Features

Challenges

Alternatives Considered

Comparison

Additional Context

BioPhoton commented 3 years ago

cc @vmasek