Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to apply metadata and decorator in Angular

2025-03-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article focuses on "how to use metadata and decorators in Angular". Interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn how to use metadata and decorators in Angular.

As a front-end framework designed for large-scale front-end projects, Angular actually has many designs worth referring to and learning from. This series is mainly used to study the implementation principles of these designs and functions. This article mainly focuses on the metadata that can be seen everywhere in Angular.

Decorators are the core concept when developing with Angular. In Angular, decorators are used to attach metadata to classes or attributes to let themselves know what those classes or attributes mean and what to do with them.

Decorator and metadata

Neither decorator nor metadata is a concept proposed by Angular. So let's take a brief look at it first.

Metadata (Metadata)

In a general concept, metadata is data that describes user data. It summarizes the basic information about the data and makes it easier to find and use specific data instances. For example, the author, creation date, modification date, and file size are very basic examples of document metadata.

In the scenario for classes, metadata is used to decorate the class to describe the definition and behavior of the class so that the expected behavior of the class can be configured.

Decorator (Decorator)

Decorator is a language feature of JavaScript and an experimental feature in stage 2 (stage 2).

Decorators are functions that are called on classes, class elements, or other JavaScript syntax forms during definition.

The decorator has three main functions:

You can replace the value being modified with a matching value with the same semantics. (for example, the decorator can replace a method with another method, one field with another, one class with another, and so on).

You can associate metadata with the value being modified; you can read it externally and use it for meta programming and self-checking.

You can provide access to the value being decorated through metadata. For common values, they can be implemented by value names; for private values, they receive accessor functions and then choose to share them.

In essence, decorators can be used to metaprogram values and add functionality to them without fundamentally changing their external behavior.

For more information, please refer to the tc39/proposal-decorators proposal.

Decorators and metadata in Angular

When we develop Angular applications, components, instructions, services, modules, etc., need to be defined and developed through decorators. The decorator appears immediately before the class definition to declare that the class has the specified type and to provide metadata appropriate to that type.

For example, we can declare the class of Angular with the following decorator: @ Component (), @ Directive (), @ Pipe (), @ Injectable (), @ NgModule ().

Use decorators and metadata to change the behavior of a class

Take @ Component () as an example, the purpose of this decorator includes:

Mark the class as an Angular component.

Provides configurable metadata to determine how the component should be processed, instantiated, and used at run time.

You can refer to how to use @ Component (), but I won't say much about it here. Let's take a look at the definition of this decorator:

/ / provide the configuration metadata interface definition of the Angular component / / in Angular, the component is a subset of instructions, and when export interface Component extends Directive {/ / changeDetection is always associated with the template for the change detection policy / / instantiation component of this component, Angular will create a change detector that propagates the binding of the component. ChangeDetection?: ChangeDetectionStrategy; / / defines a collection of injectable objects visible to its view DOM sub-objects viewProviders?: Provider []; / / the module ID that contains the module of the component, which must be able to parse the relative URL moduleId?: string; of templates and styles and the CSS-style encapsulation policy encapsulation?: ViewEncapsulation / / override the default interpolation start and end delimiters (`{{` and `}}`) interpolation?: [string, string];} / / component decorator and metadata export const Component: ComponentDecorator = makeDecorator ('Component', / / uses the default CheckAlways policy, in which change detection is automatic until it is explicitly disabled. (C: Component = {}) = > ({changeDetection: ChangeDetectionStrategy.Default,... c}), Directive, undefined, (type: Type, meta: Component) = > SWITCH_COMPILE_COMPONENT (type, meta))

The above is the definition of component decoration and component metadata. Let's take a look at the process of creating the decorator.

The process of creating a decorator

We can find it in the source code, and the decorators for components and instructions are generated through makeDecorator ():

Export function makeDecorator (name: string, props?: (. Args: any []) = > any, parentClass?: any, / / decorator name and attribute additionalProcessing?: (type: Type) = > void, typeFn?: (type: Type,... args: any []) = > void): {new (. Args: any []): any; (. Args: any []): any (. Args: any []): (cls: any) = > any;} {/ / noSideEffects is used to verify that the function wrapped by the closure compiler has no side effects return noSideEffects () = > {const metaCtor = makeMetadataCtor (props) / / decorator factory function DecoratorFactory (this: unknown | typeof DecoratorFactory,... args: any []): (cls: Type) = > any {if (this instanceof DecoratorFactory) {/ / assign metadata metaCtor.call (this, .ARGs); return this as typeof DecoratorFactory;} / / create decorator factory const annotationInstance = new (DecoratorFactory as any) (... args) Return function TypeDecorator (cls: Type) {/ / compile the class if (typeFn) typeFn (cls,.. ARGs); / / it is important to use Object.defineProperty because it creates an attribute that cannot be enumerated, thus preventing it from being copied during subclassing. Const annotations = cls.hasOwnProperty (ANNOTATIONS)? (cls as any) [ANNOTATIONS]: Object.defineProperty (cls, ANNOTATIONS, {value: []}) [ANNOTATIONS]; annotations.push (annotationInstance); / / if (additionalProcessing) additionalProcessing (cls) for specific logic execution; return cls;};} if (parentClass) {/ / inherit parent class DecoratorFactory.prototype = Object.create (parentClass.prototype) } DecoratorFactory.prototype.ngMetadataName = name; (DecoratorFactory as any). AnnotationCls = DecoratorFactory; return DecoratorFactory as any;};}

In the above example, we generate a Component decorator factory that defines the component through makeDecorator (). When you create a component using @ Component (), Angular compiles the component based on the metadata.

Compile components based on decorator metadata

Angular compiles the Angular component based on the decorator metadata and patches the generated component definition (component cmp) to the component type:

Export function compileComponent (type: Type, metadata: Component): void {/ / initialize ngDevMode (typeof ngDevMode = = 'undefined' | | ngDevMode) & & initNgDevMode (); let ngComponentDef: any = null; / / metadata may have resources maybeQueueResolutionOfComponentResources (type, metadata) that need to be parsed; / / the function used here is the same as the instruction, because this is only a subset of the metadata needed to create ngFactoryDef (type, metadata). Object.defineProperty (type, NG_COMP_DEF, {get: () = > {if (ngComponentDef = null) {const compiler = getCompilerFacade () / / parsing the component if (componentNeedsResolution (metadata)) {. / / exception handling}. / / to create the complete metadata needed for the compiled component const templateUrl = metadata.templateUrl | | `ng:///$ {type.name} / template.html` Const meta: R3ComponentMetadataFacade = {... directiveMetadata (type, metadata), typeSourceSpan: compiler.createParseSourceSpan ('Component', type.name, templateUrl), template: metadata.template | |', preserveWhitespaces, styles: metadata.styles | | EMPTY_ARRAY, animations: metadata.animations, directives: [], changeDetection: metadata.changeDetection, pipes: new Map () Encapsulation, interpolation: metadata.interpolation, viewProviders: metadata.viewProviders | | null,} / / the compilation process needs to calculate the depth to confirm whether the compilation is finally completed compilationDepth++; try {if (meta.usesInheritance) {addDirectiveDefToUndecoratedParents (type);} / / compile the component ngComponentDef = compiler.compileComponent (angularCoreEnv, templateUrl, meta) based on the metadata required by the template, environment, and component } finally {/ / make sure to reduce the compilation depth compilationDepth-- even if the compilation fails } if (compilationDepth = 0) {/ / when the NgModule decorator is executed, we queue the module definition so that the queue is dequeued only if all claims have been parsed, and adds itself as a module scope to all its declarations / / this call runs a check to see if any module in the queue can be queued And add scope to their declaration flushModuleScopingQueueAsMuchAsPossible () } / / if the component compilation is asynchronous, declare that the @ NgModule annotation of the component can be executed and set the ngSelectorScope property on the component type / / which allows the component to patch itself with the directiveDefs in the module after compilation is completed. If (hasSelectorScope (type)) {const scopes = transitiveScopesFor (type.ngSelectorScope); patchComponentDefWithScope (ngComponentDef, scopes) } return ngComponentDef;},...});}

The process of compiling a component may be asynchronous (such as a URL that needs to parse component templates or other resources). If compilation does not occur immediately, compileComponent will add resource resolution to the global queue and will not be able to return cmp until the global queue is resolved by calling resolveComponentResources.

Metadata during compilation

Metadata is information about a class, but it is not a property of the class. Therefore, the data used to configure the definition and behavior of the class should not be stored in an instance of the class, and we need to save this data elsewhere.

In Angular, the metadata generated by the compilation process is managed and maintained using CompileMetadataResolver. Here we mainly look at the logic related to instructions (components):

Export class CompileMetadataResolver {private _ nonNormalizedDirectiveCache = new Map (); / / use Map to save private _ directiveCache = new Map (); private _ summaryCache = new Map (); private _ pipeCache = new Map (); private _ ngModuleCache = new Map (); private _ ngModuleOfTypes = new Map (); private _ shallowModuleCache = new Map () Constructor (private _ config: CompilerConfig, private _ htmlParser: HtmlParser, private _ ngModuleResolver: NgModuleResolver, private _ directiveResolver: DirectiveResolver, private _ pipeResolver: PipeResolver, private _ summaryResolver: SummaryResolver, private _ schemaRegistry: ElementSchemaRegistry, private _ directiveNormalizer: DirectiveNormalizer, private _ console: Console, private _ staticSymbolCache: StaticSymbolCache, private _ reflector: CompileReflector Private _ errorCollector?: ErrorCollector) {} / / clears the metadata clearCacheFor (type: Type) for a specific instruction {const dirMeta = this._directiveCache.get (type) This._directiveCache.delete (type);...} / / clear all metadata clearCache (): void {this._directiveCache.clear ();.} / * load declared directives and pipes in NgModule * / loadNgModuleDirectiveAndPipeMetadata (moduleType: any, isSync: boolean, throwIfNotFound = true): Promise {const ngModule = this.getNgModuleMetadata (moduleType, throwIfNotFound); const loading: Promise [] = [] If (ngModule) {ngModule.declaredDirectives.forEach ((id) = > {const promise = this.loadDirectiveMetadata (moduleType, id.reference, isSync); if (promise) {loading.push (promise);}}); ngModule.declaredPipes.forEach ((id) = > this._loadPipeMetadata (id.reference));} return Promise.all (loading) } / / load instruction (component) metadata loadDirectiveMetadata (ngModuleType: any, directiveType: any, isSync: boolean): SyncAsync {/ / if loaded, return if (this._directiveCache.has (directiveType)) {return null;} directiveType = resolveForwardRef (directiveType); const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata (directiveType)! / / create instruction (component) metadata const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata | null) = > {const normalizedDirMeta = new cpl.CompileDirectiveMetadata ({isHost: false, type: metadata.type, isComponent: metadata.isComponent, selector: metadata.selector, exportAs: metadata.exportAs, changeDetection: metadata.changeDetection, inputs: metadata.inputs, outputs: metadata.outputs, hostListeners: metadata.hostListeners HostProperties: metadata.hostProperties, hostAttributes: metadata.hostAttributes, providers: metadata.providers, viewProviders: metadata.viewProviders, queries: metadata.queries, guards: metadata.guards, viewQueries: metadata.viewQueries, entryComponents: metadata.entryComponents, componentViewType: metadata.componentViewType, rendererType: metadata.rendererType, componentFactory: metadata.componentFactory, template: templateMetadata}) If (templateMetadata) {this.initComponentFactory (templateMetadata.ngContentSelectors);} / / stores complete metadata information, as well as metadata summary information this._directiveCache.set (directiveType, normalizedDirMeta); this._summaryCache.set (directiveType, normalizedDirMeta.toSummary ()); return null;} If (metadata.isComponent) {/ / if it is a component and the process may be asynchronous, you need to wait for the template after the asynchronous process to return const template = metadata.template! Const templateMeta = this._directiveNormalizer.normalizeTemplate ({ngModuleType, componentType: directiveType, moduleUrl: this._reflector.componentModuleUrl (directiveType, annotation), encapsulation: template.encapsulation, template: template.template, templateUrl: template.templateUrl, styles: template.styles, styleUrls: template.styleUrls, animations: template.animations, interpolation: template.interpolation, preserveWhitespaces: template.preserveWhitespaces}) If (isPromise (templateMeta) & & isSync) {this._reportError (componentStillLoadingError (directiveType), directiveType); return null;} / and store metadata in return SyncAsync.then (templateMeta, createDirectiveMetadata);} else {/ / instruction to store metadata createDirectiveMetadata (null) directly; return null }} / / get the metadata information of a given instruction (component) getDirectiveMetadata (directiveType: any): cpl.CompileDirectiveMetadata {const dirMeta = this._directiveCache.get (directiveType)!;. Return dirMeta;} / / gets the metadata summary information of a given instruction (component) getDirectiveSummary (dirType: any): cpl.CompileDirectiveSummary {const dirSummary = this._loadSummary (dirType, cpl.CompileSummaryKind.Directive);. Return dirSummary;}}

As you can see, during the compilation process, Map is used to store the metadata of these classes during compilation, whether they are components, instructions, pipes, or modules.

At this point, I believe you have a deeper understanding of "how to use metadata and decorators in Angular". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report