🎉 Version 3 - What's new?

Version 3 - What’s new?

An festive image celebrating the release of version 3 of the Angular Dynamic Hooks library

Version 3 marks a significant overhaul and modernization of the Angular Dynamic Hooks library. It is now fully up-to-date with Angular 17+, easier to use and has several completely new features.

The good news: Users of version 2 should only minimally need to adjust their code for the upgrade to version 3. Nevertheless, there are some breaking changes to look out for (see below)

New features

Standalone components

The whole library was converted to the more modern standalone component structure. This makes it future-proof and allows Angular to further optimize package sizes and bundling logic, such as for deferred views.

Less boilerplate

With version 3, configuring global settings/providers has become optional. To get started quickly, the you can pass the content and an array of parsers on each usage of the library individually - nothing else needed.

In addition, you can now use the component classes directly as parsers, as simple as:

parsers = [ExampleComponent];

See the new the Quickstart example for the full code.

SSR-Compatibility

The core functionality of the library has been adjusted to work with Server-Side-Rendering under Angular 17+ out of the box. You can however still implement your own PlatformService, if you have a more specific use-case.

New: HTML as content

Previously, you could only pass strings as the content input to the library. With v3, you can now also submit HTML elements directly - even document.body! For example like this:

dynamicHooksService.parse(document.body, parsers).subscribe(result => {
  // Do whatever
});

The library will look through all the child elements, search for hooks/selectors and automatically load components into the current browser content.

New: Element hooks

Related to that, you can now look for element hooks in addition to text hooks. Element hooks find and load components into HTML elements (instead of replacing text patterns), so custom parsers can use simple browser methods to find them (querySelectorAll, etc.).

The standard SelectorHookParser now also uses the new element hooks under the mood. This has the benefit that it is now able to use proper CSS selectors (.myComponent) for finding hooks instead of just tag names like before.

New: Standalone mode

With the new standalone mode, you can use the library outside of an Angular app. This means, you can load fully-functional Angular components in non-Angular contexts - for example to add “Angular widgets” to HTML generated by a CMS, static page generators, etc.

All you need is your content and a list of parsers, as always:

import { parse } from 'ngx-dynamic-hooks';
...

parse(content, parsers).then(result => {
  // Do whatever
});

New: Single component

In case you just need to load a single dynamic component in your template, a new DynamicSingleComponent was added to make this process as easy as possible.

Breaking changes

DynamicHooksModule

As the library has transitioned to standalone components, trying to use DynamicHooksModule.forRoot() will throw an error. Instead, use the new provideDynamicHooks function to create the global settings.

import { ApplicationConfig } from '@angular/core';
import { provideDynamicHooks } from 'ngx-dynamic-hooks';

export const appConfig: ApplicationConfig = {
  providers: [
    ..
    provideDynamicHooks({
      parsers: [ ... ],
      options: { ... }
    })
  ]
};

Additionally, the properties of the settings object have also been simplified:

interface DynamicHooksSettings {
  parsers?: HookParserEntry[];
  options?: ParseOptions;
  inheritance?: DynamicHooksInheritance;
}

Note that using provideDynamicHooks to create global settings is now completely optional. So you can skip this step completely if you want and instead pass the parsers individually to each DynamicHooksComponent.

DynamicHooksComponent

To use <ngx-dynamic-hooks> in your templates, you will now have to import the DynamicHooksComponent. Simply put it into the imports field of the component/module that needs it:

@Component({
  ...
  imports: [DynamicHooksComponent]
})
export class YourComponent {
...
}

Child modules

If you were previously using DynamicHooksModule.forChild(), you can now simply call provideDynamicHooks again in your child providers to register the child settings. You can do this as deeply nested as you want.

Also, note that DynamicHooksInheritance.LINEAR is now the default option (instead of ALL) as its more in line with angular’s default behaviour.

Switch to HTML-based parsing

In v2 of the library, the standard parser to find hooks used regular expressions to find them in the content. In v3, this was replaced by HTML/DOM-based parsing to improve robustness and to make it easier to write custom hooks.

If you encounter any issues with this switch and configurations that previously worked fine now throw errors, you can return to regex-based parsing by enabling it as an option in your SelectorHookParserConfig, like this:

const parsers: HookParserEntry[] = [{
  component: ExampleComponent,
  parseWithRegex: true
}];

Renamings

Several classes, interfaces, methods etc. were renamed to better reflect their new roles.

  • OutletComponent is now DynamicHooksComponent
  • OutletService is now DynamicHooksService. Also, the order of the parameters for the parse method has changed.
  • OutletParseResult is now ParseResult and returns more properties
  • OutletOptions is now ParseOptions
  • HookFinder.findStandaloneHooks is now HookFinder.findSingletagHooks. You can now also just use HookFinder.find for both singletag or enclosing hooks.

Minor changes

  • Traditional text hook parsers from version 2 remain mostly unchanged - with one exception: They will now use <dynamic-component-anchor>-elements for components by default. If you want to continue using the component selector, you must specify it manually via the hostElementTag property.
  • DynamicContentChild.componentSelector field has been removed. you can mostly accomplish the same via DynamicContentChild.componentRef.location.nativeElement.tagName.