🎉 Version 3 - What's new?

Note: You are currently browsing the documentation for an older version of the library.

Click here to switch to the newest version.

Configuration

Global settings

When importing the library via forRoot(), you can provide a DynamicHooksGlobalSettings-object to set up the global configuration. We have already done this in the Quick start example. All of those settings will be passed to OutletComponents in your app automatically. The possible values are:

Name Type Description
globalParsers HookParserEntry[] An list of hook parsers to provide to all OutletComponents
globalOptions OutletOptions An options object to provide to all OutletComponents
lazyInheritance number An enum option from DynamicHooksInheritance

Note that you don’t have to define a global settings object. You can also configure each OutletComponent with their own parsers and options as inputs.

Component bindings

These are all of the inputs you can pass to each OutletComponent (<ngx-dynamic-hooks>) individually:

Input name Type Description
content string The content string to parse and render
context any An optional object to pass data to the dynamically-loaded components
globalParsersBlacklist string[] An optional list of global parsers to blacklist, identified by their name
globalParsersWhitelist string[] An optional list of global parsers to whitelist, identified by their name
parsers HookParserEntry[] An optional list of hook parsers to use instead of the global parsers
options OutletOptions An optional options object to use instead of the global options

There is also an output you may subscribe to:

Output name Type Description
componentsLoaded Observable<LoadedComponent[]> Will trigger once all components have loaded (including lazy-loaded ones)

Each LoadedComponent from the output represents a dynamically-created component and contains some information you may find interesting:

interface LoadedComponent {
    hookId: number;                     // The unique hook id
    hookValue: HookValue;               // The hook that was replaced by this component
    hookParser: HookParser;             // The associated parser
    componentRef: ComponentRef<any>;    // The created componentRef
}

OutletOptions

You can define OutletOptions both in the global settings as well as on each OutletComponent indidually (to overwrite the global values).

These options determine the overall behaviour of the outlet, such as of how the content string is rendered and how dynamic components are managed.

Option name Type Default Description
sanitize boolean true Whether to use Angular’s DomSanitizer to sanitize the content string before output (hooks are unaffected by this)
convertHTMLEntities boolean true Whether to replace HTML entities like &amp; with normal characters
fixParagraphTags boolean true When using a WYSIWYG-editor, writing enclosing hooks may rip apart paragraph HTML (the <p>-tag starting before the hook and the corresponding </p>-tag ending inside, and vice versa). This will result in weird HTML when rendered in a browser. This setting removes these ripped-apart tags.
updateOnPushOnly boolean false Normally, the bindings of all dynamic components are checked/updated on each change detection run. This setting will update them only when the context object passed to the OutletComponent changes by reference.
compareInputsByValue boolean false Whether to deeply-compare inputs for dynamic components by their value instead of by their reference on updates
compareOutputsByValue boolean false Whether to deeply-compare outputs for dynamic components by their value instead of by their reference on updates
compareByValueDepth number 5 When comparing by value, how many levels deep to compare them (may impact performance)
ignoreInputAliases boolean false Whether to ignore input aliases like @Input('someAlias') in dynamic components and use the actual property names instead
ignoreOutputAliases boolean false Whether to ignore output aliases like @Output('someAlias') in dynamic components and use the actual property names instead
acceptInputsForAnyProperty boolean false Whether to disregard @Input()-decorators completely and allow passing in values to any property in dynamic components
acceptOutputsForAnyObservable boolean false Whether to disregard @Output()-decorators completely and allow subscribing to any Observable in dynamic components

Child modules

If you have child modules, you can use DynamicHooksModule.forChild() to import the library into them as well. Without further configuration, the child module will simply inherit the root settings.

You can however also use this function to register additional parsers and options instead of loading all of them at once via forRoot() on the main module. This works with both with eagerly imported child modules as well as lazily-loaded child modules. But there are some differences in how they behave:

In eagerly imported child modules, the forChild() parsers will be added to the global root parsers. As a result, every <ngx-dynamic-hooks> component in your app will use the same list of parsers. This ultimately behaves the same as registering all parsers via forRoot(). Meanwhile, options are merged in the order of importing them.

In lazily loaded child modules, you can modify what parsers & options are available to the child module via the optional lazyInheritance option in DynamicHooksGlobalSettings. It accepts a value from the DynamicHooksInheritance enum, which are as follows:

  1. DynamicHooksInheritance.All : The module uses all parsers & options from anywhere in the app. (default)
  2. DynamicHooksInheritance.Linear : The module only uses parsers & options from direct module ancestors (such a father and grandfather modules, but not “uncle” modules)
  3. DynamicHooksInheritance.None : The module only uses parsers & options defined by itself

If relevant, module options overwrite each other in the following order: Any module, direct ancestor modules, then own module.

An example for a lazily-loaded child module might then look like this:

import { DynamicHooksModule, DynamicHooksInheritance } from 'ngx-dynamic-hooks';

@NgModule({
  ...
  imports: [
    DynamicHooksModule.forChild({
        globalParsers: [
          {component: ChildDynamicComponent}
        ],
        globalOptions: {
            // whatever you like for this module
        }
        lazyInheritance: DynamicHooksInheritance.Linear
      })
  ],
})
export class LazilyLoadedChildModule {}

Warning

Do not use or set the lazyInheritance option to anything other than All in eagerly-loaded modules. The other options will not work as Angular near-seamlessly merges eager modules with the root module and using it will almost certainly result in unexpected behaviour.

Lazy-loading components

If you are using the Ivy templating engine (Angular 9+), you can configure your hook parsers in such a way that they lazy-load the component class only if it is needed and the corresponding hook appears in the content string.

You may have noticed that the component-property in SelectorHookParserConfig has the type ComponentConfig (see Parsers section). This means it can be the component class, but also a LazyLoadComponentConfig:

interface LazyLoadComponentConfig {
    importPromise: () => Promise<any>;
    importName: string;
}

importPromise should be a function that returns the import promise for the component while importName should be the name of the component class to be used. As the selector of the component cannot be known before loading the component class, you will also have to manually specify a selector of your choice for the hook.

The full SelectorHookParserConfig for a lazy-loaded component could then look like so:

{
    component: {
        importPromise: () => import('./components/lazyComponent.c'),
        importName: 'LazyComponent'
    },
    selector: 'app-lazy'
}

That’s all there is to it! LazyComponent will now automatically be lazy-loaded if <app-lazy>...</app-lazy> is found in the content string.

Note

importPromise must contain a function returning the import-promise, not the import-promise itself! Otherwise the promise would be executed right where it is defined, which defeats the point of lazy-loading.

Also: Due to the way Angular component creation works and to prevent bugs, the host elements of lazily-loaded components are not directly inserted into the content string, but are instead wrapped in anchor elements, which serve as placeholders until they are ready.