Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 7856

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 60k
Author
Asked: November 28, 20242024-11-28T07:44:09+00:00 2024-11-28T07:44:09+00:00

AnimationFactory: how Angular creates animation renderers

  • 60k

This article is intended to be the beginning of a short series about Angular transition animations mechanism source code, a niche in Angular codebase often considered confusing and not enough documented.


Angular transition animations

For “Transition Animations” in Angular we mean adding transitioning effects between elements' state changes.
Normally this is done by binding a trigger to a property that will get the values of the states whose transition we want to animate.
They share only last part of their flow with “Timeline Animations“, the ones executed by explicitly operating Animation Players, and that will not be a topic of these articles.

Quick recap on Angular Renderers

To manipulate the DOM without directly accessing its nodes Angular leverages Renderers, tools designed to be a level of abstraction on native DOM operations.
They're shaped upon abstract class Renderer2, and are fundamental in transition animations context because some of their flavors get used to start animation flow.
They get not instanciated manually, but generated by a RendererFactory2.

Animations module

First step to start using animations is importing BrowserAnimationsModule.
This module, among other things, will provide some concrete implementations of aforementioned abstract classes, adding animation capabilities to component creation logic.
Above all, it will ship AnimationRendererFactory that, together with some Renderer implementations it's capable to return, will supersede the ones provided by BrowserModule.
(For the sake of simplicty this series will take in account only the Browser platform, used to build webapps).

export function instantiateRendererFactory(     renderer: DomRendererFactory2, engine: AnimationEngine, zone: NgZone) {   return new AnimationRendererFactory(renderer, engine, zone); }  const SHARED_ANIMATION_PROVIDERS: Provider[] = [   {provide: AnimationBuilder, useClass: BrowserAnimationBuilder},   {provide: AnimationStyleNormalizer, useFactory: instantiateDefaultStyleNormalizer},   {provide: AnimationEngine, useClass: InjectableAnimationEngine}, {     provide: RendererFactory2,     useFactory: instantiateRendererFactory,     deps: [DomRendererFactory2, AnimationEngine, NgZone]   } ]; 
Enter fullscreen mode Exit fullscreen mode

AnimationRendererFactory creating animation renderer

The gist of this factory is that it gets injected with an AnimationEngine used by its generated renderers to enrich DOM operations with animated effects.

@Injectable() export class AnimationRendererFactory implements RendererFactory2 {   ...   constructor(       private delegate: RendererFactory2, private engine: AnimationEngine, private _zone: NgZone) 
Enter fullscreen mode Exit fullscreen mode

Another important dependency passed to its constructor, is the property called delegate typed as RendererFactory2, populated with an instance of DomRendererFactory2.
This is an implementation of Delegation Pattern used to compose an enhanced RendererFactory.
Normal non-animated DOM operations will be delegated to the original DefaultDomRenderer2 instances generated by this delegate factory, while the new delegator animated renderers will take care of animation tasks only.

Being this class a factory of renderers, its main functionalities reside in createRenderer method.
It accepts two arguments:

  • the hostElement being the “first ancestor” of all the elements included in our template (think of the element identified by your component's selector)
  • a type typed as RendererType2 (I know… sorry), an object storing some rendering information, mostly retrieved from your @Component decorator's metadata.

Looking at its early lines we can understand how a basic animation renderer is created for components not registering any animation.

createRenderer(hostElement: any, type: RendererType2): Renderer2 {   const EMPTY_NAMESPACE_ID = '';    // cache the delegates to find out which cached delegate can   // be used by which cached renderer   const delegate = this.delegate.createRenderer(hostElement, type);   if (!hostElement || !type || !type.data || !type.data['animation']) {     let renderer: BaseAnimationRenderer|undefined = this._rendererCache.get(delegate);     if (!renderer) {       // Ensure that the renderer is removed from the cache on destroy       // since it may contain references to detached DOM nodes.       const onRendererDestroy = () => this._rendererCache.delete(delegate);       renderer =           new BaseAnimationRenderer(EMPTY_NAMESPACE_ID, delegate, this.engine, onRendererDestroy);       // only cache this result when the base renderer is used       this._rendererCache.set(delegate, renderer);     }     return renderer;   } 
Enter fullscreen mode Exit fullscreen mode

  1. first it uses the factory delegate to generate a pure “animation-unaware” DOMRenderer and store it as a local delegate variable (don't get confused: global delegate this.delegate refers to factory delegate, while (unfortunately) homonymous local const delegate point to the newly generated renderer delegate)
  2. it checks for any trigger listed inside animations property of @Component's decorator (after verifying the creation request is not relative to an hostRenderer, but this detail is beyond the scope of this article), and the branch entered when it can't find anyone is the one we're investigating
  3. looks inside factory cache for an AnimationRenderer bound to the DOMRenderer just created (delegate DomRendererFactory2 is capable of returning cached DOMRenderer too, so the “created” could be a reference to an already existing one, thus giving a match in strict equality lookup used by JS for Map.prototype)
  4. if not found, creates a new one (injected with freshly created renderer delegate, and with a cache cleanup callback to be issued on renderer destruction), adds that to cache and returns it

Little work for BaseAnimationRenderer

Looking at BaseAnimationRenderer code we notice how for most of its implementations it merely calls the corresponding delegated renderer methods, aside from elements insertions and deletions related ones.

export class BaseAnimationRenderer implements Renderer2 {   constructor(       protected namespaceId: string, public delegate: Renderer2, public engine: AnimationEngine,       private _onDestroy?: () => void)    ...    appendChild(parent: any, newChild: any): void {     this.delegate.appendChild(parent, newChild);     this.engine.onInsert(this.namespaceId, newChild, parent, false);   }    insertBefore(parent: any, newChild: any, refChild: any, isMove: boolean = true): void {     this.delegate.insertBefore(parent, newChild, refChild);     // If `isMove` true than we should animate this insert.     this.engine.onInsert(this.namespaceId, newChild, parent, isMove);   }    removeChild(parent: any, oldChild: any, isHostElement: boolean): void {     this.engine.onRemove(this.namespaceId, oldChild, this.delegate, isHostElement);   } 
Enter fullscreen mode Exit fullscreen mode

This seems to be needed by deep layers of animation module to mark the elements as removed or inserted even in absence of a visible transition, mainly for move operations.
More interesting are the overrides of its child class AnimationRenderer, instanciated by the factory when the component that's gonna be created declares some animation triggers.
Both trigger registrations and added functionalities will be covered in following articles.

Thanks for reading, and feel free to get in touch in comments for every question, correction, clarification or opinion.

angulartypescriptwebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 1k
  • Popular
  • Answers
  • Author

    How to ensure that all the routes on my Symfony ...

    • 0 Answers
  • Author

    Insights into Forms in Flask

    • 0 Answers
  • Author

    Kick Start Your Next Project With Holo Theme

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.