Skip to content

Angular Portal - Expression has changed after it was checked. Previous value: 'portal: null'. Current value: 'portal: [object Object]' #18321

@GlauberF

Description

@GlauberF

Reproduction

I followed the documentation https://beta-angular-material-io.firebaseapp.com/cdk/portal/overview, however this error erupts.

Captura de Tela_selecionar área_20200128194441

Code

ts

import {
    AfterViewInit,
    Component, EventEmitter,
    InjectionToken, Injector,
    Input,
    OnDestroy,
    OnInit, Output,
    ViewContainerRef,
    ViewEncapsulation
} from '@angular/core';
import { ComponentPortal, Portal, PortalInjector, TemplatePortal } from '@angular/cdk/portal';
import { Subject } from 'rxjs';

export const PORTAL_DATA = new InjectionToken<{}>('PortalData');

@Component({
    selector: 'vimbo-renders-component-or-template-dynamically',
    templateUrl: './renders-component-or-template-dynamically.html',
    styleUrls: ['./renders-component-or-template-dynamically.scss'],
    encapsulation: ViewEncapsulation.None
})
export class VimboRendersComponentOrTemplateDynamicallyComponent implements OnInit, OnDestroy, AfterViewInit {

    selectedPortal: Portal<any>;
    componentPortal: ComponentPortal<any>;
    templatePortal: TemplatePortal<any>;

    @Input()
    componentName: any;

    @Input()
    templateName: any;

    @Input()
    inputData: {
        [K: string]: any;
    };

    @Output()
    outputData: EventEmitter<any>;

    // Private
    private _unsubscribeAll: Subject<any>;

    /**
     * Constructor
     *
     * @param {ViewContainerRef} _viewContainerRef
     * @param {Injector} injector
     */
    constructor(
        private _viewContainerRef: ViewContainerRef,
        private injector: Injector
    ) {
        // Set the defaults
        this.outputData = new EventEmitter();

        // Set the private defaults
        this._unsubscribeAll = new Subject();

        this.selectedPortal = null;
    }

    // -----------------------------------------------------------------------------------------------------
    // @ Lifecycle hooks
    // -----------------------------------------------------------------------------------------------------

    /**
     * On init
     */
    ngOnInit(): void {
        console.log(this)
    }

    /**
     * After View Init
     */
    ngAfterViewInit() {
        // INFORMED TWO INPUT, ERROR
        if (this.componentName && this.templateName) {
            console.error('Only one of the Inputs (componentName, templateName) can be informed');
        }

        // COMPONENT
        else if (this.componentName && !this.templateName) {
            this.componentPortal = new ComponentPortal(
                this.componentName,
                null,
                this.createInjector(this.inputData)
            );
            this.selectedPortal = this.componentPortal;
        }

        // TEMPLATE HTML
        else if (!this.componentName && this.templateName) {
            this.templatePortal = new TemplatePortal(
                this.templateName,
                this._viewContainerRef,
                this.createInjector(this.inputData)
            );
            this.selectedPortal = this.templatePortal;
        }
    }

    /**
     * On destroy
     */
    ngOnDestroy(): void {
        // this.selectedPortal.detach();
        // Unsubscribe from all subscriptions
        this._unsubscribeAll.next();
        this._unsubscribeAll.complete();
    }

    // -----------------------------------------------------------------------------------------------------
    // @ Public methods
    // -----------------------------------------------------------------------------------------------------

    /**
     * Create Injector
     * @param data
     */
    private createInjector(data): PortalInjector {
        const injectorTokens = new WeakMap<any, any>([
            [PORTAL_DATA, data],
        ]);

        return new PortalInjector(this.injector, injectorTokens);
    }


}

html

<ng-template [cdkPortalOutlet]="selectedPortal">
</ng-template>

another question, I have an input where the component to be rendered is informed, I would pass the name as a string and within the portal component, resolve based on the name of the component.
If not, I have to reference / import all the components therein within the portal component and pass the name of the component's class.

Environment

Angular CLI: 8.3.20
Node: 10.15.3
OS: linux x64
Angular: 8.2.14
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... platform-server, router, service-worker

Package                                    Version
--------------------------------------------------------------------
@angular-devkit/architect                  0.803.20
@angular-devkit/build-angular              0.803.23
@angular-devkit/build-optimizer            0.803.20
@angular-devkit/build-webpack              0.803.23
@angular-devkit/core                       8.3.20
@angular-devkit/schematics                 8.3.20
@angular/cdk                               8.2.3
@angular/cli                               8.3.20
@angular/flex-layout                       8.0.0-beta.27
@angular/material                          8.2.3
@angular/material-moment-adapter           8.2.3
@ngtools/webpack                           8.3.23
@nguniversal/express-engine                8.2.6
@nguniversal/module-map-ngfactory-loader   8.2.6
@schematics/angular                        8.3.20
@schematics/update                         0.803.20
rxjs                                       6.5.3
typescript                                 3.5.3
webpack                                    4.39.2

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions