Angular ElementSchemaRegistry for “dummies”
Tags: angular, angularcli
Published: 2020-02-11 16:03:23

---
title: Angular ElementSchemaRegistry for “dummies”
published: true
date: 2020-02-07 13:21:18 UTC
tags: angular, angular-cli
canonical_url: https://medium.com/angular-in-depth/angular-elementschemaregistry-for-dummies-83d54cd31478
cover_image: https://miro.medium.com/max/3629/1*ywk1dinx8SnUV17-10JFJg.jpeg
---

_How does Angular know I made an error in a component template?_

_Let's find out (CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, etc)._


_*Angular 8.2 source code is used for this article._

I bet every Angular developer met such an error message in ChromeDevTools console:

![](https://cdn-images-1.medium.com/max/687/1*Y9s2dS4RqDflGu842TE_Jg.png)<figcaption>&lt;some-custom-tag&gt;&lt;/some-custom-tag&gt;</figcaption>

Usually, this means you forgot to add a component to the module — so Angular doesn’t know anything about it.  
Sometimes this error is shown on native HTML tags:

![](https://cdn-images-1.medium.com/max/675/1*b2ql9JwYe6xZuaHLW36GvA.png)<figcaption>&lt;button data-someAttr<strong>=”</strong>`{{someValue}}`<strong>” </strong>&gt;<strong>Save</strong>&lt;/button&gt;</figcaption>

You can watch these errors by running article demo project [here](https://github.com/kievsash/ElementSchemaRegistryDemo/tree/1_Watch_The_Error_in_console).

Yes, we can solve it by using **[addr.someAttr]=”someValue”** notation, but how does Angular know that button doesn’t have such an attribute? And can we somehow omit Angular checking for our specific cases (custom elements our of Angular or some specific attributes)?  
This is time to introduce [**ElementSchemaRegistry**](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/element_schema_registry.ts) from ‘[@angular/compiler](https://github.com/angular/angular/tree/8.2.x/packages/compiler)’ package.

> **_A more elaborate version of this article_** [**_is available here_**](https://indepth.dev/getting-inside-angulars-element-schema-registry-mechanism/) **_._**

### Prolog

Angular uses two ways to check component templates to be good:  
1. Generating Type Checking Blocks — you can read more about it in [Alexey Zuev](https://medium.com/u/d59a9e801370) article [here](https://medium.com/angular-in-depth/type-checking-templates-in-angular-viewengine-and-ivy-77f8536359f5).

2. Angular compiler instance uses TemplateParser which runs DomElementSchemaRegistry (implementation of **_ElementSchemaRegistry_** ) methods for checking of template AST ([Abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)) nodes validity.

_ElementSchemaRegistry_ is an abstract class that defines an interface for schema class for Angular to check its component templates (if there is such an element or if such attr exists for a specific element, etc). Here it is:

```ts
import {SchemaMetadata, SecurityContext} from '../core';

export abstract class ElementSchemaRegistry {

abstract hasProperty (tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean;

abstract hasElement (tagName: string, schemaMetas: SchemaMetadata[]): boolean;

abstract securityContext(elementName: string, propName: string, isAttribute: boolean): SecurityContext;

abstract allKnownElementNames(): string[];

abstract getMappedPropName(propName: string): string;

abstract getDefaultComponentElementName(): string;

abstract validateProperty(name: string): {error: boolean, msg?: string};

abstract validateAttribute(name: string): {error: boolean, msg?: string};

abstract normalizeAnimationStyleProperty(propName: string): string;

abstract normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string|number): {error: string, value: string};

}
```

As you can see that it has specific methods to check if some element or some attribute exists: _hasElement_ and _hasAttribute_.

How does it work under-the-hood?

### DomElementSchemaRegistry extends ElementSchemaRegistry

For browser elements checking we have a special class [DomElementSchemaRegistry](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/dom_element_schema_registry.ts). All DOM entities and its respective attributes are defined there. Here it is:

```ts
const SCHEMA: string[] = ['[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' + /* added manually to avoid breaking changes */      
...
' **base** ^[HTMLElement]|href,target', 

'**body**^[HTMLElement]|aLink,background,bgColor,link,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,text,vLink',
 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value', 

'canvas^[HTMLElement]|#height,#width', 'content^[HTMLElement]|select', 

'dl^[HTMLElement]|!compact', 'datalist^[HTMLElement]|', 

'details^[HTMLElement]|!open', 'dialog^[HTMLElement]|!open,returnValue', 

'dir^[HTMLElement]|!compact', 'div^[HTMLElement]|align', 

'embed^[HTMLElement]|align,height,name,src,type,width', 

'fieldset^[HTMLElement]|!disabled,name',
...

'span^[HTMLElement]|', 

'style ^[HTMLElement]|!disabled,media,type', 

'caption^[HTMLElement]|align', 

'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width', 

'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width', 

'**table**^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width', 

' **tr** ^[HTMLElement]|align,bgColor,ch,chOff,vAlign', 

'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign', 

'template ^[HTMLElement]|', 
'textarea ^[HTMLElement]|autocapitalize,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap', 

'title^[HTMLElement]|text', 

'track^[HTMLElement]|!default,kind,label,src,srclang', 

'ul^[HTMLElement]|!compact,type', 'unknown^[HTMLElement]|', 

'video^media|#height,poster,#width',
...
```

And the list is created by exporting browser IDL (interface description language) as we can see from [compiler-cli](https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli)/[src](https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src)/[ngtsc](https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc)/[typecheck](https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc/typecheck)/[src](https://github.com/angular/angular/tree/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc/typecheck/src)/[dom.ts](https://github.com/angular/angular/blob/3959511b80ff8abb55b8c8858a6080472bff1589/packages/compiler-cli/src/ngtsc/typecheck/src/dom.ts#L63) file comments (one of the places where [DomElementSchemaRegistry](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/schema/dom_element_schema_registry.ts) is instantiated).

```
`DomElementSchemaRegistry`, a schema * maintained by the Angular team via extraction from a browser IDL.
```

OK, so we have a list of DOM entities and set of methods for checking where an HTML template is valid. How does Angular use it?

### How is DomElementSchemaRegistry used?

#### **a) AotCompiler (**[angular](https://github.com/angular/angular/tree/8.2.x)/[packages](https://github.com/angular/angular/tree/8.2.x/packages)/[platform-browser-dynamic](https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic)/[src](https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic/src)/[compiler_factory.ts](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler_factory.ts)**)**

1. Typescript _ts.createProgram_ starter needs compiler instance from compiler factory function
2. compiler_factory (lets review AOT) creates _schemaParser_ and feed it to _new TemplateParser(…)_ statement and then templateParser instance are fed to compiler instance constructor ([here](https://github.com/angular/angular/blob/f8096d499324cf0961f092944bbaedd05364eea1/packages/compiler/src/aot/compiler_factory.ts#L86-L88))

```ts
export function createAotCompiler (

compilerHost: AotCompilerHost, options: AotCompilerOptions,

errorCollector?: (error: any, type?: any) =>

void): {compiler: AotCompiler, reflector: StaticReflector} {

...

const elementSchemaRegistry = new DomElementSchemaRegistry();

const tmplParser = new TemplateParser(

config, staticReflector, expressionParser, **elementSchemaRegistry** , htmlParser, console, []);

...

const compiler = new AotCompiler (

config, options, compilerHost, staticReflector, resolver, **tmplParser** ,

new StyleCompiler(urlResolver), viewCompiler, typeCheckCompiler,

new NgModuleCompiler(staticReflector),

new InjectableCompiler(staticReflector, !!options.enableIvy), new TypeScriptEmitter(),

summaryResolver, symbolResolver);

return {compiler, reflector: staticReflector};

}
```

3.   
a) Сompiler instance has two(actually more but we review these two) methods **__compileComponent_** and **__createTypeCheckBlock._**  
b) These methods call [_parseTemplate](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler.ts#L647) where templateParserInstance.[parse](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/template_parser/template_parser.ts#L89) is performed.

c) And it starts template parsing process (btw [visitor pattern](https://www.cse.wustl.edu/~cytron/cacweb/Tutorial/Visitor/) is used to iterate over AST nodes) where our _DomElementSchemaRegistry_ instance and TemplateParseVisitor instance are used to check whether AST node (which represents template element) is valid.

For example [here](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/template_parser/template_parser.ts#L687) and [here](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/template_parser/template_parser.ts#L743) you can see code that represents messages at the beginning of the article:

```ts
private _assertElementExists(matchElement: boolean, element: html.Element) {

const elName = element.name.replace(/^:xhtml:/, '');

if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) {

let errorMsg = `'${elName}' is not a known element:\n`;

errorMsg +=

`1. If '${elName}' is an Angular component, then verify that it is part of this module.\n`;

if (elName.indexOf('-') > -1) {

errorMsg +=

`2. If '${elName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`;

} else {

errorMsg +=

`2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;

}

this._reportError(errorMsg, element.sourceSpan !);

}
```

#### **b) JIT compiler (**[angular](https://github.com/angular/angular/tree/8.2.x)/[packages](https://github.com/angular/angular/tree/8.2.x/packages)/[platform-browser-dynamic](https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic)/[src](https://github.com/angular/angular/tree/8.2.x/packages/platform-browser-dynamic/src)/[compiler_factory.ts](https://github.com/angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts)**)**

AOT compiler creates an instance for TemplateParser [explicitly](https://github.com/angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler_factory.ts#L87) (you remember that parser then uses schemaRegistry (instance of DomElementSchemaRegistry)).

```ts
//angular/angular/blob/8.2.x/packages/compiler/src/aot/compiler_factory.ts

...
const elementSchemaRegistry = new DomElementSchemaRegistry();

const tmplParser = new TemplateParser(config, staticReflector, expressionParser, elementSchemaRegistry, htmlParser, console, []);
...
```

For instantiating JIT Compiler Angular uses Injector to create respective dependencies instances to its [compiler_factory](https://github.com/angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts#L162).

```ts
// angular/angular/blob/8.2.x/packages/platform-browser-dynamic/src/compiler_factory.ts

export const COMPILER_PROVIDERS = <StaticProvider[]>[
...
{ provide: DomElementSchemaRegistry, deps: []},  
{ provide: ElementSchemaRegistry, useExisting: DomElementSchemaRegistry},
...
]
```

This means that for JIT build we can substitute DomElementSchemaRegistry with some custom ElementSchema class (we will talk about it later).

Now we know how it works. And? What to do with this knowledge?

### Use cases

[Continue reading...](https://medium.com/angular-in-depth/angular-elementschemaregistry-for-dummies-83d54cd31478)


*** 

I am preparing my future video-course with advanced techniques of mastering Angular/RxJS. Want to get a notification when it is done? Leave your email here (and get free video-course on RxJS unit testing): http://eepurl.com/gHF0av

Like this article? Follow me on [Twitter](https://twitter.com/El_Extremal)!