Skip to content

🤖 A collection of tools to make your Angular views more modular, scalable, and maintainable

License

Notifications You must be signed in to change notification settings

ngneat/overview

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

90 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


npm MIT Commitizen friendly PRs styled with prettier All Contributors ngneat spectator

Overview - The Template for Success in Template Management

Compatibility with Angular Versions

@ngneat/overview Angular
6.x >= 17
5.x >= 16
4.x >= 14 < 16
3.x >= 13 < 14
2.x >= 11 < 13

Installation

npm i @ngneat/overview
yarn add @ngneat/overview

Table of Contents

DynamicView

Use the dynamicView structural directive to render a component, a template, HTML, or default template dynamically.

Let’s say we build a generic error component. What we want is to give our consumers the flexibly to create it by using one of three options:

  • They can choose to use the default template
  • They can choose to use their own text which can be static or dynamic
  • They can choose to pass their own template or component
import { DynamicViewDirective, Content } from '@ngneat/overview';

@Component({
  template: ` <div *dynamicView="view">Default view</div> `,
  standalone: true,
  imports: [DynamicViewDirective],
})
export class ErrorComponent {
  @Input() view: Content | undefined;
}

You can also pass a context or an injector as inputs to the directive:

<h5>Component</h5>
<ng-container
  *dynamicView="component; 
    injector: myInjector; 
    context: { $implicit: 'my title' }"
/>

<h5>Template</h5>
<ng-template #tpl let-title>
  <b>{{ title }}</b>
</ng-template>

<ng-container *dynamicView="tpl; context: { $implicit: 'my title' }" />

If you pass context to a component and the value can be accessed via the injectViewContext function:

import { injectViewContext } from '@ngneat/overview';

interface Context {
  title: string;
}

@Component({
  template: `<div>{{ context().title }}</div>`,
  standalone: true,
})
export class MyDynamicComponent {
  context: Signal<Context> = injectViewContext<Context>();
}

injectViewContext returns a readonly signal with the view's context object.

Teleporting

Teleporting means rendering a view at a different location from its declaration. There are two cases it might be helpful:

  • Avoid prop drilling to a nested component.
  • Rendering a view at another place in the DOM while keeping its context where it’s defined.

You can read more about this approach in this article.

Use the teleportOutlet directive to define a new outlet. An outlet is an anchor where the view will be projected as a sibling.

import { TeleportOutletDirective } from '@ngneat/overview';

@Component({
  template: `
    <div class="flex">
      <ng-container teleportOutlet="someId"/>
    </div>
  `,
  standalone: true,
  imports: [TeleportOutletDirective],
})
export class FooComponent {}

Use the teleportTo directive to teleport the view to a specific outlet:

import { TeleportDirective } from '@ngneat/overview';

@Component({
  template: `
    <section *teleportTo="someId">
      {{ value }}
    </section>
  `,
  standalone: true,
  imports: [TeleportDirective],
})
export class BarComponent {
  value = '...';
}

ViewService

The ViewService provides facade methods to create modular views in Angular. It's been used in various projects like hot-toast, and helipopper.

createComponent

The createComponent method takes a Component, and returns an instance of CompRef:

import { ViewService, CompRef } from '@ngneat/overview';

@Injectable()
class ToastService {
  private viewService = inject(ViewService);
  componentRef: CompRef;

  init() {
    this.componentRef = this.viewService
      .createComponent(HotToastContainerComponent)
      .setInput('defaultConfig', defaultConfig)
      .appendTo(document.body);
  }
}

There are cases where we want to use an Angular component template in a third-party library that takes a native DOM element or a string. In this case, we can use the getRawContent or the getElement method, respectively.

import { ViewService } from '@ngneat/overview';

@Directive()
class ChartDirective {
  private viewService = inject(ViewService);

  createChart(color: string) {
    const ref = this.viewService.createComponent(FooTooltip).setInput('color', color).detectChanges(document.body);

    const content = ref.getRawContent();

    ref.destroy();

    Highcharts.chart('container', {
      tooltip: {
        formatter: function () {
          return content;
        },
        useHTML: true,
      },
    });
  }
}

createComponent Options

createComponent<Comp, Context>(component: Type<Comp>, {
  injector?: Injector,
  environmentInjector?: EnvironmentInjector,
  context?: Context,
  vcr?: ViewContainerRef,
})

createTemplate

The createTemplate method takes a TemplateRef, and returns an instance of ViewRef.

createTemplate<Context>(tpl: TemplateRef<Context>, {
  context?: Context,
  vcr?: ViewContainerRef,
  injector?: Injector,
})

createView

The createView method takes a Component, a TemplateRef or a string, and creates a View:

import { ViewService, Content } from '@ngneat/overview';

@Injectable()
class ToastService {
  private viewService = inject(ViewService);

  createToast(content: Content) {
    const ref = this.viewService.createView(content);
    document.body.appendChild(ref.getElement());
  }
}

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Netanel Basal

💻 🤔

Dharmen Shah

🖋 📖

This project follows the all-contributors specification. Contributions of any kind welcome!

Icons made by Freepik from www.flaticon.com