Add custom elements and attributes to compiler schema

1.9k Views Asked by At

There are some custom elements and attributes in component template (in this example they are used by third-party non-Angular code):

<foo></foo>
<div data-bar="{{ bar }}"></div>

They cause a compiler error:

Template parse errors:
'foo' is not a known element:
1. If 'foo' is an Angular component, then verify that it is part of this module.
2. If 'foo' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("
    [ERROR ->]<foo></foo>
    <div data-bar="{{ bar }}"></div>
  "): App@1:4
Can't bind to 'bar' since it isn't a known property of 'div'. ("
    <foo></foo>
    <div [ERROR ->]data-bar="{{ bar }}"></div>
  ")
...

How can foo element and data-bar attribute be added to compiler schema?

NO_ERRORS_SCHEMA is not an option because it is not desirable for other unknown elements and attributes to be whitelisted.

3

There are 3 best solutions below

2
yurzui On BEST ANSWER

You can try to override DomElementSchemaRegistry like this:

import { DomElementSchemaRegistry, ElementSchemaRegistry } from '@angular/compiler'
import { SchemaMetadata } from '@angular/core';

const MY_DOM_ELEMENT_SCHEMA = [
  'foo'
];

const MY_CUSTOM_PROPERTIES_SCHEMA = {
  'div': {
    'bar': 'string'
  }
};

export class CustomDomElementSchemaRegistry extends DomElementSchemaRegistry {
  constructor() {
    super();
  }

  hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean {
    return MY_DOM_ELEMENT_SCHEMA.indexOf(tagName) > -1 || 
         super.hasElement(tagName, schemaMetas);
  }

  hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean {
    const elementProperties = MY_CUSTOM_PROPERTIES_SCHEMA[tagName.toLowerCase()];
    return (elementProperties && elementProperties[propName]) || 
        super.hasProperty(tagName, propName, schemaMetas);
  }
}

platformBrowserDynamic().bootstrapModule(AppModule, {
  providers: [{ provide: ElementSchemaRegistry, useClass: CustomDomElementSchemaRegistry }]
});

Plunker Example

4
Conor Tierney On

Looking good, but notice in you Plunker Example, the div in the markup is as below:

data-bar="{{ bar }}"

bar being = "test" is outputting on the dom just as:

<div></div>

and not

<div data-bar="test"></div>

If you want the attribute rendered - say for polymer components you can use the following.

[attr.data-bar]="bar" in the markup
2
kisp On

Injecting CustomDomElementSchemaRegistry works now-a-days a little-bit differently:

JIT compiler

The injector has changed since the previous answer, so the type signature changed a very little. From version 5 StaticInjector needs the deps array with useClass (Angular 8.2)

platformBrowserDynamic().bootstrapModule(AppModule, 
 {
   providers: [
    { 
       provide: ElementSchemaRegistry, 
       useClass: CustomDomElementSchemaRegistry, 
       deps: [] 
    }
   ]
 }
);

AOT compiler

We can NOT use the injector for ahead of time compilation, but there is a possiblity to "monkey-patch" the class DomElementSchemaRegistry before it is instantiated by the compiler, for a detailed description check it here:

https://medium.com/angular-in-depth/angular-elementschemaregistry-for-dummies-83d54cd31478