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

"Design a function that generates a return type based on the input array

Created a function similar to this one // window.location.search = "?id1=123&id2=ABC&id3=456" const { id1, id2, id3 } = readArgsFromURL("id1", {name: "id2", required: false}, {name: "id3", required: true}) ...

Guide on integrating external libraries with Angular CLI

I've been working on incorporating external libraries into my project, and I've been following the instructions provided here. While I know it's possible to use CDNs in my index.html, I'm interested in learning how to do it using TypeS ...

The parameter cannot be assigned the readonly type 'X' in this context

I encountered an issue with a third-party library where the class structure changed. Initially, it was defined as: export class Foo { field: X[]; …. } In my code, I was working with this type: print(foo.field) After updating to a new version, the c ...

Encountering the error "unrecognized pseudo-class :lang" following the Angular update

Recently, I updated my node version and encountered a new error: custom-file-input:lang(en)~.custom-file-label -> unmatched pseudo-class :lang I'm not sure what's causing this issue. Can someone help me troubleshoot this error? My current n ...

What is the function of the Angular dependency injection mechanism?

I'm trying to grasp the inner workings of Angular/NestJs dependency injection. It's intriguing how the type of parameters gets lost when a Typescript class is constructed. For example: type Dependency1 = {}; type Dependency2 = {}; class X { ...

Looking for a youtube.d.ts file to integrate the youtube-iframe-api with Angular 2?

My current challenge involves implementing the youtube iframe api for seamless video snippet display and control within an Angular 2 application. Maintaining TypeScript's type concept is crucial for both the webpack compiler and myself :). A brief ov ...

Guide to transmitting a boolean value from a service to a component using observables in Angular

How can I pass a boolean value from a service to a component? This boolean switches between true and false in two functions: onStartBlockUI and onStopBlockUI. In onStartBlockUI, the boolean is set to true, while in onStopBlockUI, it is set to false. I need ...

Angular: Unable to access values of non-existent data (reading '0')

I'm encountering an error when trying to import an excel file using the following code Angular Ag Grid Excel Import Cannot read properties of undefined (reading '0') I'm attempting to import a file named Book.csv, and wondering if thi ...

Obtaining the display name and phone numbers of a contact

Using the Ionic Contacts Native feature, I am able to retrieve a list of contacts from my phone. .ts: import { Contacts } from 'ionic-native'; ///////// export class ContactPage { contactsfound = [] constructor(public navCtrl: NavCont ...

Angular - combining lowercase letters in an attribute

Hello, I'm new to using Angular and currently working on creating an attribute within a div tag. I have successfully achieved this task. However, I am in need of changing my input to lowercase during the concatenation. <!--"Fade" Slider--> < ...

Material 2's portal host fails to display the specified template portal

Check out the Demo Plunker here: https://plnkr.co/edit/Ye8MAUmrMEQKnPt93TjT?p=preview I'm following the guidance from Material 2 documentation in order to establish a template portal that will be displayed within a nearby host (although my ultimate g ...

How to send parameters with the fetch API

After completing a task that involved converting code from Angular HttpClient to using fetch API, I encountered an issue with passing parameters. Below is the original code snippet before my modifications: let activeUrl = new URL(this.serverAddress); ...

The dependencies of Nest are unable to be resolved by the system

Attempting to implement AuthService within UsersService and UsersService within AuthService results in a "circular dependency" issue. The error message states that "Nest can't resolve dependencies of the AuthService (UserModel, JwtService, ?). Please ...

What methods can I utilize to manage the output generated by the C# backend project?

I'm new here and I'm thrilled to be asking my first question! :) Currently, I am working on a car rental project using C# for the backend and Angular for the frontend. I have encountered an issue while trying to register a new user with existing ...

Ionic Angular and Capacitor causing a black screen post social login

I've hit a roadblock with a challenge that I've been working on for the past few days. I am in the process of developing an application using Ionic Angular and Capacitor, allowing users to log in with their Google and Facebook social media accoun ...

Troubleshooting the creation of migration paths in NestJS with TypeORM

After diligently studying the NestJS and TypeORM documentation, I have reached a point where I need to start generating migrations. While the migration itself is creating the correct queries, it is not being generated in the desired location. Currently, m ...

Leveraging JSON data in subsequent GET request in Ionic 3

My application receives input, concatenates it to a string, and then requests JSON data. The response includes the following first two lines: Now, I need to update my code to be asynchronous. It should make the initial call, wait for a response, retrieve ...

Utilize Angular 2 to search and filter information within a component by inputting a search term from another component

In my application, I have a Component named task-board which contains a table as shown below: <tr *ngFor="let task of tasks | taskFilter: searchText" > <td>{{ task.taskName }}</td> <td>{{ task.location }}</td> <td>{{ ta ...

Struggling to find the definition of a Typescript decorator after importing it from a separate file

Consider the following scenario: decorator.ts export function logStuff(target: Object, key: string | symbol, descriptor: TypedPropertyDescriptor<any>) { return { value: function (...args: any[]) { args.push("Another argument ...

Is it possible to globally modify the component reference <dropdown-component> name in Angular during runtime in a dynamic manner?

I am currently working on an application that utilizes a component called "dropdown-component" throughout its pages. However, due to specific business requirements, I have been tasked with replacing "dropdown-component" with "custom-dropdown-component". Un ...