Содержание
Authoring a wrapper library is pretty straightforward, and you can reference other ones, especially @angular-react/fabric
.
The basic steps are as follows:
-
Create a standard Angular library (
ng generate library my-lib
) -
Add
@angular-react/core
to yourpeerDependencies
. -
For each component in the underlying UI library, as a set of components (that go together), create an
NgModule
as follows (we're using"my"
as the prefix in this example):import { registerElement } from '@angular-react/core'; import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; import { MyCounter } from 'my-react-ui-library'; import { MyCounterComponent } from './counter.component'; const components = [MyCounterComponent]; @NgModule({ declarations: components, exports: components, schemas: [NO_ERRORS_SCHEMA], }) export class MyCounterModule { constructor() { registerElement('MyCounter', () => MyCounter); } }
And the accompanying component:
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, Renderer2, ViewChild, } from '@angular/core'; import { InputRendererOptions, JsxRenderFunc, ReactWrapperComponent, } from '@angular-react/core'; import { MyCounterProps } from 'my-react-ui-library'; @Component({ selector: 'my-counter', exportAs: 'myCounter', template: ` <Counter #reactNode [count]="count" [RenderCount]="renderCount && onRenderCount" > </Counter> `, styles: ['react-renderer'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class FabBreadcrumbComponent extends ReactWrapperComponent<MyCounterProps> implements OnInit { @ViewChild('reactNode') protected reactNodeRef: ElementRef; @Input() count?: MyCounterProps['count']; @Input() renderCount?: InputRendererOptions<MyCountRenderProps>; @Output() readonly onIncrement = new EventEmitter<{ count: number }>(); onRenderCount: ( props?: MyCountRenderProps, defaultRender?: JsxRenderFunc<MyCountRenderProps> ) => JSX.Element; constructor( elementRef: ElementRef, changeDetectorRef: ChangeDetectorRef, renderer: Renderer2 ) { super(elementRef, changeDetectorRef, renderer, { setHostDisplay: true }); } ngOnInit() { this.onRenderCount = this.createRenderPropHandler(this.renderCount); } }
For further explanation see ReactWrapperComponent, as well as the inline-documentation in the code.
Conventions
- Pick a prefix for your wrapper library, and use it in your modules, components (including selectors) etc.
- e.g.
@angular-react/fabric
uses "fab" as the prefix, meaning that everyNgModule
and@Component
is prefixed withFab*
, and every selector is prefixed withfab-*
- e.g.
extend
ReactWrapperComponent
in your components, to gain the benefits of things handled in@angular-react/core
- See more conventions in ReactWrapperComponent
Templates
Wrapper component templates are a hybrid between Angular and React. To be more specific, they're Angular templates, with React component type names as their elements. See more details on this in component registry.
In addition, the Angular compiler prevents some stuff from being defined as Input
s, namely - you can't have @Input
s prefixed with on
:
Binding to event property 'onFoo' is disallowed for security reasons, please use (Foo)=... If 'onFoo' is a directive input, make sure the directive is imported by the current module.Angular
This error, and the suggested workaround are not applicable in our use-case, since they do need to be inputs (for example, function that return a value, or render props).
Therefore, as a convention - any Input
which is PascalCased is prefixed with on
when inheriting from ReactWrapperComponent.
In addition - there's one reserved prop name that can't be used - key
, since it's already being used by React, and we leverage that in more ways that usual internally.
Lastly, when extend
-ing ReactWrapperComponent there are a few more restrictions, see there for more details.