HackMD
    • Create new note
    • Create a note from template
    • Sharing Link copied
    • /edit
    • View mode
      • Edit mode
      • View mode
      • Book mode
      • Slide mode
      Edit mode View mode Book mode Slide mode
    • Customize slides
    • Note Permission
    • Read
      • Only me
      • Signed-in users
      • Everyone
      Only me Signed-in users Everyone
    • Write
      • Only me
      • Signed-in users
      • Everyone
      Only me Signed-in users Everyone
    • Commenting & Invitee
    • Publishing
      Please check the box to agree to the Community Guidelines.
      Everyone on the web can find and read all notes of this public team.
      After the note is published, everyone on the web can find and read this note.
      See all published notes on profile page.
    • Commenting Enable
      Disabled Forbidden Owners Signed-in users Everyone
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Invitee
    • No invitee
    • Options
    • Versions and GitHub Sync
    • Transfer ownership
    • Delete this note
    • Note settings
    • Template
    • Save as template
    • Insert from template
    • Export
    • Dropbox
    • Google Drive Export to Google Drive
    • Gist
    • Import
    • Dropbox
    • Google Drive Import from Google Drive
    • Gist
    • Clipboard
    • Download
    • Markdown
    • HTML
    • Raw HTML
Menu Note settings Sharing Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Transfer ownership Delete this note
Export
Dropbox Google Drive Export to Google Drive Gist
Import
Dropbox Google Drive Import from Google Drive Gist Clipboard
Download
Markdown HTML Raw HTML
Back
Sharing
Sharing Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Comment & Invitee
Publishing
Please check the box to agree to the Community Guidelines.
Everyone on the web can find and read all notes of this public team.
After the note is published, everyone on the web can find and read this note.
See all published notes on profile page.
More (Comment, Invitee)
Commenting Enable
Disabled Forbidden Owners Signed-in users Everyone
Permission
Owners
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Invitee
No invitee
   owned this note    owned this note      
Published Linked with GitHub
Like BookmarkBookmarked
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Design Doc - Component Render Strategies author: [Michael Hladky] created: 2020-02-03 status: [draft | in review | approved | **implemented** | obsolete] doc url: https://hackmd.io/42oAMrzYReizA65AwQ7ZlQ feedback url: https://github.com/ngrx/platform/issues/2441 design doc template version: 1.0.0 ## Objective <!-- A rough overall explanation of the idea as well as related terms and a quick scatch of the need. --> The goal of this document is to implement an efficient way to coalesce renderings of the view in applications. This is especially of help, in zone-less Angular applications that trigger rendering manually, but also enables us to get zone-less performance in zone-full environments. > As this document uses specific terminology find detailed explanations in the gist here: [Reactive Angular Terminology](https://gist.github.com/BioPhoton/e8e650dc3b8a7798d09d3a66916bbe10). Coalescing means buffering a set of emissions into a smaller set of those emissions. [Here a general explaination](https://gist.github.com/BioPhoton/e8e650dc3b8a7798d09d3a66916bbe10#coalescing-of-work). In the documents manner, it always coalesces all emissions to the last emission of a tick in the event loop scheduled as a microtask. A more technical explanation of the behavior would be very similar to the already existing [RxJS rate-limiting operators](https://rxjs.dev/api/operators/throttle#see-also). Here some [diagrams](https://twitter.com/Michael_Hladky/status/1119277221087150081) so "see" the difference in behavior. Angular itself has implemented several things to enable coalescing. - Flags for ngZoneEventCoalescing - scheduleTick called from ApplicationRef.tick() - scheduleDetectChanges called in elements Those mostly target rendering. Some general things where this solution can be useful are: - state derivations - state updates - rendering - event computations The general goals are: - to create a coalescing logic that improves the performance of reactive and/or zone-less applications. - It should be flexible to configure and easy to use. The technical goal is to create an RxJS operator for a configurable coalescing strategy: - acts as a filter/throttle operator for emitted values. The actual work is done with the coalesced result - enables execution-context scoping (animationFrame, Promise, setTimeout) - enables usage-context scoping (window, Class, component instance, LEmbeddedView) The real challenge here is to find the right usage-context for scoping. Here an [collection/explaintion of thoughts](https://docs.google.com/presentation/d/1QBx3nTyDpQvmNnN5NNV6apJO70rSN6lxCKS-OmPPPA8/edit#slide=id.g6e7b315996_0_226) and some edge cases. ### Expected User/Developer Experience <!-- Describe what will the user be able to do and how will they be able to do it thanks to this new feature. This should be done in a story format, described from the user’s/developer’s perspective, with as many details as possible but without going into the implementation details. --> The developer of a reactive application knows exactly when to rerener. Currently Angular takes over this decision by relying on the zone mechanism only. With this feature the developer would be able to use pipes, directives in the template or operators in the class to triger rendering. With the provided strategies the developers are able to specify how they want to render and which performance optimisations should br used. As there is quite a bit knowledge needed to use the right combintions the strategies are designed to provide an intuitively names set of strategies that ease this decision and take away the in-depth understanding. This enables developer to get zone-less performance even within zonefull applications. ### Background <!-- Stuff one needs to know to understand this doc: motivating examples, previous versions, and problems, links to related projects/design docs, etc. You should mention related work if applicable. This is background; do not write about ideas to solve problems here. --> Relevant material to read first: - [Angular ComponentView and EmbeddedView in Ivy ](https://hackmd.io/X1wy5Y1TSIWibaHiaAHyBA?view) In: Hackmd, Michael Hladky. ### Prior Art To implement the described feature 3 sections need to be understood: - Change detection in Angular - Render methods of Angular - The coalescing mechanism in general - Coalescing and Render Methods in Agnular #### Change Detection in Angular - Default, - OnPush, dirty flagging - Zone, #### Render Methods and Behaviour This information is needed to get an overview of Angulars render methods and it'S differences. The tables describe the following: - Render Method: Name and location of the methods responsible for rendering - Renders: Position in the component tree from where it will render. The app means from the root of the tree, component means from the component on which the method is called downwards. - ZoneLess: If a zone environment is needed. More specifically is the token `ngZone` is derived from `NgZone` or `NgNoopZone`. - Coalesce Scope: Angular v8 and v9 both have variations where coalescing is present and scoped in ApplicationRef - Execution: Tells how the method is executed. e.g. microtask **>=ViewEngine** | Render Method | Renders | | ------------------ | -------- | | cdRef.markForCheck | App | | markForCheck+tick | App | | cdRef.detectChange | Children | **Ivy** | Render Method | Renders | | ------------------ | -------- | | ɵmarkDirty | App | | ɵdetectChange | Children | An interesting thing is `ChangeDetectorRef#markForCheck`. Internally it runs `ApplicationRef#tick` to render the app. It is triggered by logic related to `ngZone`. This menas dirty flagging of a compoent and rendering can be used independent from each other in `NgNoopZone` environments. #### The Coalescing Mechanism in General To understand this section we split it into 2 parts. - Coalescing in general - Scoped Coalescing **General coalescing** The basic idea of coalescing and also the current implementation in Angular holds a global flag that determines if synchrounous code is running and schedules a micro or macro task that runs some logic related to rendering after all the sync cond is executed. Primitive implementation: ```typescript export function getCoalesceFn() { const isCoalescing = false; return (work: () => void) => { // If work is already scheduled to do nothing if (isCoalescing) { return; } // If NO work is scheduled // request a new animationFrame and set isCoalescing to a truthy value isCoalescing = executionContext(() => { // Reset requestAnimationFrameId isCoalescing = false; // Logic here will get buffered in the micro task queue and executed only ones work(); }); } } // Example: const doCoalesce = getCoalesceFn(); doCoalesce(() => console.log(('1')); doCoalesce(() => console.log(('2')); doCoalesce(() => console.log(('3')); doCoalesce(() => console.log(('4')); // 4 ``` **Scoping** If we think a step further, we can see that this implementation includes a sort of scope in which the coalescing is active. In the abofe implementation it was global. e.g. scoped by the window object. On a component level the will lead to situations where only the first compoent will get rerendered, others not as the scoping does not know aboul components. This leads to the need of a local scope. coalescing should be possible on component level. To enable that we need to be able to share a scope tied to the components view. A promitive solution of a shared scope can look like that: ```typescript export interface CoalesceConfig { context?: {isCoalescing: boolean}; } export function getCoalesceFn(cfg: CoalesceConfig = {isCoalescing: boolean} ) { return (work: () => void) => { if (cfg.isCoalescing) { return; } cfg.isCoalescing = executionContext(() => { cfg.isCoalescing = false; work(); }); } } // Example: const scope1 = {isCoalescing: false}; const doCoalesce1 = getCoalesceFn(scope1); const scope2 = {isCoalescing: false}; const doCoalesce2 = getCoalesceFn(scope2); doCoalesce1(() => console.log(('1')); doCoalesce1(() => console.log(('2')); doCoalesce2(() => console.log(('3')); doCoalesce2(() => console.log(('4')); // 2, 4 ``` As this lib targets mainly reactive code we would need to put this logic into an RxJS operator. To get a better idea I put some psudo code of how it could look. RxJS Operator Version: ```typescript // Basic behaviour from([1,2,3,4]).pipe( coalesce(cfg), ).subscribe(console.log); // 4 // Real-life usage stateChanges$.pipe( tap(v => updateModel(v)), coalesce(cfg), tap(_ => renderView()) ).subscribe(); ``` ### Coalescing and Render Methods in Agnular The tables describe the following: - Render Method: Name and location of the methods responsible for rendering - Renders: Position in the component tree from where it will render. The app means from the root of the tree, component means from the component on which the method is called downwards. - ZoneLess: If a zone environment is needed. More specifically is the token `ngZone` is derived from `NgZone` or `NgNoopZone`. - Coalesce Scope: Angular v8 and v9 both have variations where coalescing is present and scoped in ApplicationRef - Execution: Tells how the method is executed. e.g. microtask **>=ViewEngine** | Render Method | Renders | Zone Type | Coalesce Scope | Execution | | ------------------ | -------- | ---------- | -------------- | ---------- | | cdRef.markForCheck | App | NgZone | ApplicationRef | Micro Taks | | markForCheck+tick | App | NgNoopZone | ApplicationRef | Micro Taks | | cdRef.detectChange | Children | NgNoopZone | None | Sync Taks | **Ivy** | Render Method | Renders | Zone Type | Coalesce Scope | Execution | | ------------------ | -------- | ---------- | -------------- | -----------| | ɵmarkDirty | App | NgNoopZone | ApplicationRef | Micro Taks | | ɵdetectChange | Children | NgNoopZone | None | Sync Taks | Especially interesting are the columns ZoneLess and Execution. Zone Type is relevant for migrations to a zone-less environment. Execution is very important in terms of custom coalescing strategies, as the could influence each other badly if both are used together. Performance Features and Incompatibilities Due to some incompatibilities or the combinations are just useless not all Angular built in features can be combined with the ngrx/component performace features. **Working Combinations:** - \>=ViewEngine | | Render Method | Coalescing | Coalesce Scope | | -------- | -------------------| ---------- | -------------- | | ZoneFull | cdRef.markForCheck | ❌ | None | | ZoneLess | markForCheck+tick | ❌ | None | | ZoneLess | cdRef.detectChange | ❌ | None | | ZoneLess | cdRef.detectChange | ✔️ | None | | ZoneLess | cdRef.detectChange | ✔️ | Compoennt | - Ivy | | Render Method | Coalescing | Coalesce Scope | | -------- | -------------------| ---------- | -------------- | | ZoneFull | ɵmarkDirty | ❌ | None | | ZoneLess | ɵmarkDirty | ❌ | None | | ZoneLess | ɵdetectChange | ✔️ | None | | ZoneLess | ɵdetectChange | ✔️ | LView | ## Strategy Design ### Cases to Consider - **Push Pipe** - 01. One single-shot observable bound by one ngrxPush as template expression ```htmlmixed {{sync1$ | ngrxPush}} ``` - 02. One single-shot observable bound by multiple ngrxPush as template expression ```htmlmixed {{sync1$ | ngrxPush}} {{sync1$ | ngrxPush}} {{sync1$ | ngrxPush}} ``` - 03. Multiple single-shot observables bound by multiple ngrxPush as template expression ```htmlmixed {{sync1$ | ngrxPush}} {{sync2$ | ngrxPush}} {{sync3$ | ngrxPush}} ``` - 04. One sync multi-shot observables bound by multiple ngrxPush as template expression ```htmlmixed {{syncArray1$ | ngrxPush}} ``` - 05. One sync multi-shot observables bound by multiple ngrxPush as input binding ```htmlmixed <child1 [value]="sync1$ | ngrxPush"></child> ``` <app-push-child05 [value]="value1$ | ngrxPush: cfg"></app-push-child05> - 11. One single-shot observable bound by one ngrxPush as input binding ```htmlmixed <child1 [value]="sync1$ | ngrxPush"></child> ``` - 12. One single-shot observable passed directly to input binding rendered over ngrxPush ```htmlmixed <child [value]="sync1$"></child> ``` - 13. One single-shot observable bound by multiple ngrxPush as input binding ```htmlmixed <child [value]="sync1$ | ngrxPush"></child> <child [value]="sync1$ | ngrxPush"></child> <child [value]="sync1$ | ngrxPush"></child> ``` - 14. Multiple single-shot observables bound by multiple ngrxPush as input binding ```htmlmixed <child [value]="sync1$ | ngrxPush"></child> <child [value]="sync2$ | ngrxPush"></child> <child [value]="sync3$ | ngrxPush"></child> ``` - 21. One single-shot observable bound by one ngrxPush as input binding. The nested components uses ngrxPush to render changes. ```htmlmixed <child [value]="sync1$ | ngrxPush"> <!-- Internal Template Start --> {{sync1$ | ngrxPush}} <!-- Internal Template End --> </child> ``` - **Let Directive** - 01. One single-shot observable bound by one ngrxLet as input binding with as syntax ```htmlmixed <ng-container *ngrxLet="sync1$ as sync1">{{sync1}}</ng-container> ``` - 02. One single-shot observables bound by multiple ngrxLet as input binding with as syntax ```htmlmixed <ng-container *ngrxLet="sync1$ as sync1">{{sync1}}</ng-container> <ng-container *ngrxLet="sync1$ as sync1">{{sync1}}</ng-container> <ng-container *ngrxLet="sync1$ as sync1">{{sync1}}</ng-container> ``` - 03. Multiple single-shot observables bound by multiple ngrxLet as input binding with as syntax ```htmlmixed <ng-container *ngrxLet="sync1$ as sync1">{{sync1}}</ng-container> <ng-container *ngrxLet="sync2$ as sync2">{{sync2}}</ng-container> <ng-container *ngrxLet="sync3$ as sync3">{{sync3}}</ng-container> ``` - 11. One single-shot observable bound by one ngrxLet as input binding with let syntax ```htmlmixed <ng-container *ngrxLet="sync1$; let sync1">{{sync1}}</ng-container> ``` - 12. One single-shot observables bound by multiple ngrxLet as input binding with let syntax ```htmlmixed <ng-container *ngrxLet="sync1$; let sync1">{{sync1}}</ng-container> <ng-container *ngrxLet="sync1$; let sync1">{{sync1}}</ng-container> <ng-container *ngrxLet="sync1$; let sync1">{{sync1}}</ng-container> ``` - **TemplateRef injection** @TODO consider more edge cases - 01. @TODO inters text https://ng-run.com/edit/vbcTXFp9cVQtsigapDs2?open=app%2Fapp.component.html&aot=true ```htmlmixed <insertion [template]="ref"> <!-- Internal Template Start --> <ng-container [ngTemplateOutlet]="template"> </ng-container> <!-- Internal Template End --> </insertion> <ng-template #ref> <span>{{obs$ | push}}</span> </ng-template> ``` - **Push Pipe and Let Directive Mixed Setup** @TODO consider more edge cases - 01. One single-shot observable bound by one ngrxPush and one ngrxLet as input binding ```htmlmixed <ng-container *ngrxLet="sync1$ as sync1">{{sync1}}</ng-container> <child [value]="sync1$ | ngrxPush"></child> ``` - 02. Kitchensink :D ```htmlmixed {{nums1$ | ngrxPush}} <child [value]="nums1$ | ngrxPush"></child> <p *ngFor="let num of nums1$ | ngrxPush">{{num}}</p> <ng-container *ngIf="nums1$ | ngrxPush as sync1">{{sync1 | json}}</ng-container> <ng-container *ngrxLet="nums1$ as sync1">{{sync1 | json}}</ng-container> ``` - **Push Pipe and Let Directive and Class Based Change Detection** @TODO consider more edge cases - 01. One single-shot observable bound by one ngrxPush and one ngrxLet as input binding and in the component there is CD ```typescript sync1$.pipe( tap(() => detetChange(this)) ) .subscribe(); ``` ```htmlmixed <ng-container *ngrxLet="sync1$ as sync1">{{sync1}}</ng-container> <child [value]="sync1$ | ngrxPush"></child> ``` ### Strategies A strategy is considered as a composition of render methods of the used engine and the additional performance features. Factors impacting the performance are: - the render method - the coalescing feature - the coalesce scope ![](https://i.imgur.com/qYvDXJV.png) **@TODO this section needs to check-in form of code and working examples** Another relevant information is that Angular's coalescing features - markForCheck => @TODO name the exact method and execution context - markDirty => @TODO name the exact method and execution context are compatible with the new coalescing feature. More specific to the actual duratonSelector (the unpatched animationFrame). The strategy names are related to their impact and also serve as a roadmap like listing. **What about:** - VE/I - Options for ViewEngine / Ivy - mFC - `cdRef.markForCheck` - dC - `cdRef.detectChanges` - ɵMD - `ɵmarkDirty` - ɵDC - `ɵdetectChanges` - LV - `LView` - C - `Component` | Name | ZoneLess VE/I | Render Method VE/I | Coalescing VE/I | |-------------| --------------- | ------------------------ | ----------------- | | `asyncLike` | ❌/❌ | mFC / mFC | ❌ | | `global` | ❌/✔️ | mFC / ɵMD | ❌ | | `local` | ✔️/✔️ | dC / ɵDC | ✔️ + C/ LV | | `noop` | ❌/❌ | no rendering | ❌ | **asyncLike** This strategy is the drop-in replacement for Angular's built-in `async` pipe. This is the only strategy that **does not also work in zone-less environments**. **global** **local** **noop** This strategy does nothing but asignin the values to the view context. Handy for debugging. ## Prototype Scoping by LView: ![](https://i.imgur.com/YZgyMR4.png) ![](https://i.imgur.com/LhW2ZiE.png) ![](https://i.imgur.com/JL97WIF.png) ### Minimal Design <!-- Include only the essential parts of the code. No error handling, no performance consideration. This section should help to understand the essential implementation in one small piece of code --> Created a configurable RxJS operator to coalesce the emissions. Roughly speaking, this operator should act as a rate-limiting operator on the event-loop level. Buffer all synchronous emitted values and send the last of them as the first microtask. **Usage in the Push Pipe** ```typescript // Implementation @Pipe({ name: 'ngrxPush', pure: false }) export class PushPipe implements PipeTransform { private renderedValue?: unknown; private subscription: Subscription = new Subscription(); constructor(protected cdRef: ChangeDetectorRef) { } transform<T>(obs: Observable<T> | Promise<T> | null | undefined): T | null { // Use unpatched verson of animation frame const durationSelector = () => generateFrames( (window as any).__zone_symbol__requestAnimationFrame, (window as any).__zone_symbol__cancelAnimationFrame ); const config = {context: this.cdRef['_lView'] as any}; this.subscription.add(obs.pipe( coalesce(durationSelector, config), tap({ next(valueToRender: unknown) { this.renderedValue = valueToRender; this.cdRef.detectChanges(); } }) ) .subscribe()); return this.renderedValue; } ngOnDestroy() { this.subscription.unsubscribe(); } } ``` ```htmlmixed <!-- Real-life usage --> <!-- Coalescing Context Off --> Renders component 3 times: {{value | ngrxPush}} {{value | ngrxPush}} {{value | ngrxPush}} <!-- Coalescing Context On --> Renders component 1 time: {{value | ngrxPush}} {{value | ngrxPush}} {{value | ngrxPush}} ``` ### Detailed Design <!-- Include some important cases. Error handling, and consider how to deal with e.g. performance. This section should help to understand the tricky implementations in more detail. Don't take too much attention to typing if it bloats the code too much. --> ```typescript export interface CoalescingContext { isCoalescing: any | undefined; } export interface CoalesceConfig { context?: CoalescingContext; leading?: boolean; trailing?: boolean; } const cfg: CoalesceConfig = { context: LComponentView; executionContextRef: (window as unknown).__zone_symbol__requestAnimationFrame; }; // @TODO put suggestion for the strategy pattern ``` ### Caveats <!-- You may need to describe what you did not do or why simpler approaches don't work. Mention other things to watch out for (if any). Security Considerations How you’ll be secure. Considerations should include sanitization, escaping, existing security protocols and web standards, permission, user privacy, etc. None --> **Coalescing context in viewing** The actual strategy for coalescing within a context is only working in ivy. **As any context object can be passed and will be patched** We have to apply the flag `isCoalescing` over a symbol to the context avoid conflicts with other present properties. **Used inside a zone.js patched environment** ```typescript // Unpatched requestAnimationFrame reference window.__zone_symbol__requestAnimationFrame; ``` ### Performance Considerations <!-- Try to describe under which conditions the suggested solution is performant and which factors play the key role when starting to get bad performance. Describe a specific situation in which we can run into performance problems If possible provide a POC or a theoretical explanation of a possible solution. --> As this design doc is focusing only on performance this section is not applicable. ## Documentation Plan <!-- Try to describe the important parts of the implementation and how to documented it e.g. importance, a priority by relevance for user, level of detail, example needed. --> As the scope of this part of the package is quite small I consider the documentation in text and some examples in the demo app and as code snippets is everything we need. Following operators needed to get documented: - generateFrames - coalesce - detectChanges - markDirty - markForCheck ### Style Guide Impact <!-- Does the documentation influence the way the current style guide is structured? Also, does the new documentation introduces any technical implementations of the docs? If so please name them and give a detailed description of the impact and if possible some POCs. --> The needed documentation has no impact on the current way the documentation is maintained. ## Developer Impact ### Public API Surface Impact <!-- Are you adding new public APIs? If so, what's the change? (method signature changes, new classes/interfaces, new npm packages, config file properties or new config files altogether) Are the new APIs consistent with the existing APIs? Do they follow naming conventions and use correct terminology? --> The new behavioral RxJS operator does not affect the existing API interface. It will get introduced under a new package `@ngrx/component`. It exposes only the operator and needed interfaces and types. Following dependencies are needed: - @rxjs@>=6.5.4 The new operator is named `coalesce`. It is used the same way as any other RxJS operator is used `o$.pipe(coalesce(cfg))`. The term 'coalesce' is used to refer to its direct meaning. 'Notifications' refers to [RxJS Notification](https://github.com/ReactiveX/rxjs/blob/388c4852948660abcff22d7b82ccb0a29c77428c/src/internal/Notification.ts#L31) and 'ExecutionContext' refers to the task stacks in the event loop (sync, micro, macro) This tries to give the user an intuitive understanding of its behavior out of the operators' name itself. ### Developer Experience Impact <!-- How will this change impact developer experience? Are we adding new tools developers need to learn? Are we asking developers to change their workflows in any way? Are we placing any new constraints on how the developer should write the code to be compatible with this change? --> This has no impact on a user's workflow but can increase performance and enables them to run zone-less. If the users decide to disable zone he may need to get more familiar with RxJS operators. Other than this the user does not need to adapt or change anything. In addition to that, the created operators can be used anywhere else too, to solve related problems. ### Breaking Changes The new pipe will not introduce any breaking changes. The new behavior can be implemented in the underlying `cdAwaer` class and no public API needs to be touched. The configuration is done over pipe parameters or globally over some tokens. And will get introduced wich the above adoption. ### Rollout Plan <!-- Are the implementation delays about to negatively affect or delay the release of other features or increase the size. --> The operator can be rolled out in the following way: **Alpha releases of the operator** include only animationFrame as coalescing frame and scoping by a context. Shipped Features: - scope coalescing by context object `CoalesceConfig.context` - define coalescing by executin context `CoalesceConfig.executionContextRef` - control the emission of leading values `CoalesceConfig.leading` - control the emission of trailing values `CoalesceConfig.trailing` **Beta releases of the @ngrx/component package** parts of the package include a configuration argument to opt-in the operator and its mechanism. **Rc and official releases of the @ngrx/component package** parts of the package have the feature opt-out by default. Alos internal interfaces etc can be exposed in this releases. ### Rollback Plan <!-- How do you plan to roll back the change if major issues are found? --> In the late alpha versions, we introduce the operator. If any critical problems occur in the alpha version we can just don't ship it further versions. Until the first beta, we should have to clarify this. This is the moment we introduce the configuration option for the part of the package that uses this operator. The feature can be rolled back by removing the option from the config object. If a major issue is found in RC or even official release we can remove the parts of the package from the package and disable the related config options. There are no implementation details that could delay the release of other packages. Also, the bundle size of other features will not increase by introducing this feature. ### Maintenance Plan <!-- Explain how this library will be maintained going forward in releases after the initial release. This includes not only releases of the subject of this document but also respects its dependencies. --> The code relies on some RxJS operators naming: `Observable, filter, tap` `tap` may break in the future if Observabls make it in the standard the most probably will have callbacks as arguments instead of an observer object. But this is also a minor refactoring. ## Alternatives considered <!-- Include alternate design ideas you tried out, but didn't continue with them. List their disadvantages or at least why you did not invest more time in researching them. --> **Pipe:** - context: PipeClass, executionContextRef: animationFrame Fails if multiple components are nested @TODO double check **Directive:** - context: DirectiveClass, executionContextRef: animationFrame Fails if multiple `*ngrxLet` directives are in the same template context ## Work Breakdown <!-- Explain how multiple people would actively working on the suggested code base. If needed include branching suggestions or the way code interacts --> As the pipes scope is small enough to get maintained by one person at a time we don't need to describe the breakdown of work. ## Resources **Research Paper/Design Docs/StackBlitz/Video/Podcast/Blog/Tweet/Graphic:** - [Angular9 ivy change-detection preview](https://alexzuza.github.io/angular-9-ivy-change-detection-preview/) - [Examples of the coalescing mechanism and its behavior](https://stackblitz.com/edit/rxjs-coalescing-of-work?file=index.html) In: StackBlitz, Michael Hladky. Hladky. - [Coalescing of Change Detection](https://docs.google.com/presentation/d/1QBx3nTyDpQvmNnN5NNV6apJO70rSN6lxCKS-OmPPPA8/edit?usp=sharing) In: Google Slides, Michael Hladky. - [Reduce Change Detection Cycles with Event Coalescing in Angular](https://netbasal.com/reduce-change-detection-cycles-with-event-coalescing-in-angular-c4037199859f) In: Medium, Netanel Basal, 2019-12-10. - [How can I change the viewContainerRef of an ng-template](https://stackoverflow.com/questions/60117068/how-can-i-change-the-viewcontainerref-of-a-ng-template/60126090#60126090) In: StackOverflow - [Ivy Minor Changes](https://docs.google.com/document/d/1Dije0AsJ0PxL3NaeNPxpYDeapj30b_QC0xfeIvIIzgg/preview) In: Google Docs, Angular. **Github Pull Request/Issue/Doc/Source Link:** - [Angular change detection spec](https://github.com/angular/angular/blob/9bd959076730c4e22ceadda73694198b4f01b9e0/packages/core/test/acceptance/change_detection_spec.ts) - [LComponentView vs LEmbeddedView](https://github.com/ngrx/platform/pull/2046#issuecomment-581167650) - [getNativeRequestAnimationFrame](https://github.com/angular/angular/blob/44623a116158c8048ce61a873ed63a290c039202/packages/core/src/util/raf.ts) - [add a flag in bootstrap to enable coalesce event to improve performance](https://github.com/angular/angular/pull/30533/files#diff-9d549aa403494fd043a146b8db64f503R264-R275) - [RFC: Component: Evaluation of using angular's internal `ɵdetectChanges` method](https://github.com/ngrx/platform/issues/2050) - [isAngularZone](https://github.com/angular/angular/blob/8.2.14/packages/core/src/zone/ng_zone.ts#L143-L144) - [coalesce change detection](https://github.com/angular/angular/blob/master/packages/core/src/zone/ng_zone.ts#L264) - [Angulars scheduleDetectChanges](https://github.com/angular/angular/blob/master/packages/elements/src/component-factory-strategy.ts#L202-L215) - [CHANGELOG v9](https://github.com/angular/angular/blob/master/CHANGELOG.md#breaking-changes)

Import from clipboard

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lost their connection.

Create a note from template

Create a note from template

Oops...
This template is not available.


Upgrade

All
  • All
  • Team
No template found.

Create custom template


Upgrade

Delete template

Do you really want to delete this template?

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Tutorials

Book Mode Tutorial

Slide Mode Tutorial

YAML Metadata

Contacts

Facebook

Twitter

Discord

Feedback

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions

Versions and GitHub Sync

Sign in to link this note to GitHub Learn more
This note is not linked with GitHub Learn more
 
Add badge Pull Push GitHub Link Settings
Upgrade now

Version named by    

More Less
  • Edit
  • Delete

Note content is identical to the latest version.
Compare with
    Choose a version
    No search result
    Version not found

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub

      Please sign in to GitHub and install the HackMD app on your GitHub repo. Learn more

       Sign in to GitHub

      HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Available push count

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully