K
K
Kovalsky2019-09-02 16:45:36
Angular
Kovalsky, 2019-09-02 16:45:36

How to pass object reference as compileContext?

I found on the Internet the directive code for dynamically creating components, it uses Compiler:compileModuleAndAllComponentsAsync , ViewContainerRef:createComponent and other scary words:

compile.directive.ts

import {
    Compiler, NgModule, Component, Input, ComponentRef, Directive,
    ModuleWithComponentFactories, OnChanges, Type,
    ViewContainerRef
} from '@angular/core';
import { CommonModule } from '@angular/common';

@Directive({
    selector: '[compile]'
})
export class CompileDirective implements OnChanges {
    @Input() compile: string;
    @Input() compileContext: any;

    compRef: ComponentRef<any>;

    constructor(private vcRef: ViewContainerRef, private compiler: Compiler) {}

    ngOnChanges() {
        if(!this.compile) {
            if(this.compRef) {
                this.updateProperties();
                return;
            }
            throw Error('You forgot to provide template');
        }

        this.vcRef.clear();
        this.compRef = null;

        const component = this.createDynamicComponent(this.compile);
        const module = this.createDynamicModule(component);
        this.compiler.compileModuleAndAllComponentsAsync(module)
            .then((moduleWithFactories: ModuleWithComponentFactories<any>) => {
                let compFactory = moduleWithFactories.componentFactories.find(x => x.componentType === component);

                this.compRef = this.vcRef.createComponent(compFactory);
                this.updateProperties();
            })
            .catch(error => {
                console.log(error);
            });
    }

    updateProperties() {
        for(var prop in this.compileContext) {
            this.compRef.instance[prop] = this.compileContext[prop];
        }
    }

    private createDynamicComponent (template:string) {
        @Component({
            selector: 'custom-dynamic-component',
            template: template,
        })
        class CustomDynamicComponent {}
        return CustomDynamicComponent;
    }

    private createDynamicModule (component: Type<any>) {
        @NgModule({
            // You might need other modules, providers, etc...
            // Note that whatever components you want to be able
            // to render dynamically must be known to this module
            imports: [CommonModule],
            declarations: [component]
        })
        class DynamicModule {}
        return DynamicModule;
    }
}


It is used something like this:
// Шаблон компонента
<ng-container *compile="template; context: { some_key: true }"></ng-container>

// Объявление свойства template в текущем компоненте
template = `
  <div *ngIf="some_key">This shit finally works, nice</div>
`;

Performing this miracle on the current component will successfully render the <div>This shit finally works, nice</div>.
The main question is how to pass an object to be used as compileContext ? Well, or that the object was passed as a property to compileContext
And it would be great if a couple of things that were not clear to me were clarified:
1. Why is this directive structural?
2. How does context map to compileContext in general ? What mechanism handles the string "template; context: { some_key: true }"?
Thanks in advance!
A small update: this is not about AOT, but about JIT.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Anton Shvets, 2019-09-02
@lazalu68

1. So shorter. The structural directive is sugar.
2. Because it
unfolds into this

<ng-template [compile]="template" [compileContext]="{ some_key: true }">
  <ng-container></ng-container>
</ng-template>

Pass it straight
A small update: There is a suspicion that this directive will not work during AOT compilation, because there is no compiler. Which makes it completely useless.

V
Vasily Mazhekin, 2020-03-29
@mazhekin

Angular HTML compiler
Here is a component to which you can pass a dynamic template in a string and an object with data. It also works in the prod, where in the main module you need to register the module for jit once

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question