Angular 5: Unable to add a destroyed View to a ViewContainer

I encountered a new error in my Angular application that I haven't seen before. The issue is arising from this specific line in the Angular source code.

This error occurs when I log out and then log back into my app. While on a certain route, there is an observable being observed. When this observable emits a value to its stream, it triggers updates and change detection, resulting in elements being removed and added dynamically.

I suspect the component below is causing the error. Can anyone identify any obvious mistakes I'm making? I have included what I believe could be relevant to the bug and commented out everything else. It's possible that I am misusing ngTemplateOutlet and ContentChild here...

Component:

@Component({
    selector: 'paginated-infinite-scroll',
    templateUrl: './paginated-infinite-scroll.component.html'
})
export class PaginatedInfiniteScrollComponent {
    // ...

    @ContentChild('results') resultsRef: TemplateRef<any>;
    @ContentChild('noResults') noResultsRef: TemplateRef<any>;
    @ContentChild('loading') loadingRef: TemplateRef<any>;
    @ContentChild('loadingMore') loadingMoreRef: TemplateRef<any>;

    // ...
}

Template:

<div infiniteScroll [infiniteScrollDistance]="2" [infiniteScrollThrottle]="300" [scrollWindow]="true" (scrolled)="scrolled()">
    <ng-container *ngIf="result.items.length > 0 && !loading">
        <ng-template [ngTemplateOutlet]="resultsRef"></ng-template>
    </ng-container>

    <ng-container *ngIf="result.items.length === 0 && !loading">
        <ng-template [ngTemplateOutlet]="noResultsRef"></ng-template>
    </ng-container>

    <ng-container *ngIf="loading">
        <ng-template [ngTemplateOutlet]="loadingRef"></ng-template>
    </ng-container>

    <ng-container *ngIf="loadingMore">
        <ng-template [ngTemplateOutlet]="loadingMoreRef"></ng-template>
    </ng-container>
</div>

Usage:

<paginated-infinite-scroll [onScroll]="onScroll" [query]="query" [result]="result">
    <ng-template #results>
        // Display results looped through here...
    </ng-template>

    <ng-template #noResults>
        // Show message for no results here...
    </ng-template>

    <ng-template #loading>
        // Display loading spinner here...
    </ng-template>

    <ng-template #loadingMore>
        // Show alternative loading spinner here...
    </ng-template>
</paginated-infinite-scroll>

Answer №1

After investigation, I discovered that the bug was not originating from the code mentioned in my initial query. The issue stemmed from an observable present within a directive. I had neglected to unsubscribe from this observable. The directive was set up to subscribe to authentication events, and whenever there was a login/logout action, it would attempt to add or remove the connected component accordingly. However, since the component was being destroyed upon navigation, the bug manifested itself.

Here is the logic implemented in my directive:

@Directive({
    selector: '[loggedIn]'
})
export class LoggedInDirective implements OnInit, OnDestroy {
    private which: boolean = false;
    private _loggedIn: boolean = false;

    view = this.templateRef.createEmbeddedView(null);
    authEventsSub: Subscription;

    constructor(
        private viewContainerRef: ViewContainerRef,
        private templateRef: TemplateRef<any>
    ) {}

    ngOnInit() {
        this.authEventsSub = this.authEventsService.events$.subscribe((event: AuthEvent) => {
            this._loggedIn = (event === AuthEvent.LOGGED_IN) || (event === AuthEvent.READY);
            this.render();
        });
    }

    private render() {
        if (this.which) {
            if (this._loggedIn) {
                this.viewContainerRef.insert(this.view);
            } else {
                this.viewContainerRef.detach();
            }
        } else {
            if (this._loggedIn) {
                this.viewContainerRef.detach();
            } else {
                this.viewContainerRef.insert(this.view);
            }
        }
    }

    // ...
}

To address the issue, I added an unsubscribe call to the observable in the ngOnDestroy hook:

authEventsSub: Subscription;

constructor(
    private viewContainerRef: ViewContainerRef,
    private templateRef: TemplateRef<any>,
    private authEventsService: AuthEventsService,
) {}

ngOnInit() {
    this.authEventsSub = this.authEventsService.events$.subscribe((event: AuthEvent) => {
        this._loggedIn = (event === AuthEvent.LOGGED_IN) || (event === AuthEvent.READY);
        this.render();
    });
}

ngOnDestroy() {
    if (this.authEventsSub) {
        this.authEventsSub.unsubscribe();
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to showcase a unique date for every item that gets added to a list?

I am new to using React, so please bear with me. I want to be able to show the date and time of each item that I add to my list (showing when it was added). I am struggling to get this functionality working with my current code. Any help or explanation o ...

Exploring Angular Performance: Template Functions vs. Properties. Which Method Reigns Supreme in Speed?

When dealing with multiple input controls that need to display a message when touched and invalid, I created a function for re-use in my form. The form contains numerous controls, so re-usability is important. isInvalid = (control: AbstractControl): boole ...

angular-oauth2-oidc - Issue with missing 'State' and 'Scope' parameters

One crucial requirement set by the identity server is to refrain from including 'state' and 'scope' in the URL. The specified request format is as follows URL?app=xxx&response_type=code&client_id=yyy&state=zzz&redirect_ ...

What is the necessity behind keeping makeStyles and createStyles separate in Material UI with TypeScript?

Dealing with TypeScript issues in Material UI has been a frequent task for me, and I find it frustrating that styling components requires combining two different functions to create a hook every time. While I could use a snippet to simplify the process, it ...

Create a function in JavaScript that is able to accept a variable number of objects as arguments

I have a good grasp of how to pass infinite parameters in a function in JavaScript. But what about accepting any number of objects as parameters in a function? This is my current implementation: function merge<T>(objA: T, objB: T){ return Object. ...

By utilizing a function provided within the context, React state will consistently have its original value

After passing functions from one component to its parent and then through context, updating the state works fine. However, there is an issue with accessing the data inside these functions. They continue to show as the initial data rather than the updated v ...

Encountering issues when trying to import const enums in a webpack project using babel

Currently, I am working on a react project that was initially created using create-react-app. However, we later ejected and made extensive modifications to the webpack configuration. My current struggle lies in importing const enums from external libraries ...

React-router-dom TypeScript error when defining the type of the prop parameter in the loader

I'm having trouble with the params prop in the loader prop within the routes. I've defined the params in TypeScript, but I'm getting errors that I don't understand. Any help would be appreciated, thanks in advance. I tried to use the Cu ...

Issue with react router v6: Component fails to render even though route has been changed

The router seems to be experiencing an issue where it does not render a component. Specifically, on the home page, the Private Route is only rendered once. Clicking on a NavLink changes the URL to "/agreements", but the component itself is not being render ...

What is the best way to address dynamic meta tags in Angular server-side rendering (SSR

My Angular application uses server side rendering, and I am dynamically loading the meta tags content from the backend. Despite seeing the proper content in the HTML, Facebook's debugger does not seem to recognize the tags. Has anyone else encountered ...

There seems to be an issue in Angular as it is unable to retrieve /

I'm encountering an issue with my simple application where I am receiving the error message "Cannot GET /." Also, in the console, I see this error: TypeError: Cannot read property 'checked' of null at Object.SL_BBL_locer. I'm unsure ab ...

When utilizing *ngIf in Angular 2, the issue of #elmentRef remaining undefined still persists

When using #elementReference with *ngIf in Angular 2, the element reference remains undefined even after the *ngIf expression evaluates to true. In this example, the value of the input will not be displayed. <input *ngIf="true" type="text" #myRef (inpu ...

What is the best way to run tests on this method using Jest?

import { format, getDaysInMonth, getMonth, getYear, isValid, parse } from "date-fns"; export class DateService { public getDaysInMonth(month?: Date) { return getDaysInMonth(month || new Date()); } What is the best way to test this func ...

`How can I incorporate typography into my Angular Material design?`

For my angular app, I am developing a custom theme and incorporating bootstrap's reboot to establish a basic starting point for non-material elements. To avoid conflicts with material variables, I converted the reboot into a mixin. In this mixin, I pa ...

Setting a blank value or null equivalent to a field: Tips and tricks

Here is the component state that I am working with: interface Person { name: string; surname: string; } interface CompState{ //...fields ... person?: Person; } render() { if(this.state.person){ const comp = <div>{this. ...

Connecting Angular components to the Document Object Model (DOM)

In previous versions of AngularJS, you could create a directive and link it to an existing controller, allowing you to use the same controller for multiple directives with different templates. angular .module('App') .component('Name', ...

The error at core.js line 4442: TypeError - Unable to access the 'trim' property of an undefined value

I encountered an issue with the 'trim' function in my Angular Node MySQL CRUD Application core.js:4442 ERROR TypeError: Cannot read property 'trim' of undefined at AdmissionListComponent.post (admission-list.component.ts:30) at Admissi ...

Multiple asynchronous calls in Angular 2

In my Component, there is a function that is supposed to return a value (Promise). This value requires information from two distinct sources: an API call and data from a database. The method in question looks like this: public getValue(): Promise<numb ...

Enhancing Readability of Public Static Member Variables in Typescript

In my node application, I am utilizing typescript and winston for logging purposes. One key element of my setup is the "Logger" class which consists of a "logger" member and an "init()" function. By exporting this class, I understand that the "logger" memb ...

Tips for displaying only the initial 15 characters of a text value

The content extracted from a .ts file is being displayed on the home.html page. I am aiming to display only the initial 15 characters followed by 3 dots (...). Despite my efforts, the following code snippet is not functioning as expected: home.html < ...