Observable doesn't respond to lazy loaded module subscriptions

I am trying to understand why my lazy loaded module, which loads the test component, does not allow the test component to subscribe to an observable injected by a test service.

index.ts

export { TestComponent } from './test.component';
export { TestModule } from './test.module';

test.component.ts

import { Component, OnInit } from '@angular/core';
import { TestService } from './test.service';
import { Observable } from 'rxjs';

@Component({
    selector: 'test',
    template: `
        <p>{{test | async}}</p>
  `,
})
export class TestComponent {
    test: Observable<number>;
    constructor(private testService: TestService) { 
        this.test = this.testService.getObservable();
    }
}

test.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TestComponent } from './test.component';
import { TestService } from './test.service';

@NgModule({
    declarations: [TestComponent],
    imports: [
        CommonModule,
    ],
    providers: [TestService]
})
export class TestModule { }

test.service.ts

import { Injectable } from '@angular/core';
import { from, Observable } from 'rxjs';
import { delay } from "rxjs/operators";

@Injectable()
export class TestService {
    getObservable(): Observable<number> {
        return from([...Array(10).keys()].reverse())
            .pipe(
                delay(1000)
            )
    }
}

app.component.ts

import { Component, ViewChild, ViewContainerRef, Compiler, Injector, Type, NgModuleFactory } from '@angular/core';
import { TestModule } from './test';

@Component({
    selector: 'app-root',
    template: `
        <ng-container #vc></ng-container>
  `,
    styles: []
})
export class AppComponent {
    @ViewChild('vc', { read: ViewContainerRef }) containerRef: ViewContainerRef;

    constructor(private compiler: Compiler, private injector: Injector) {
    }

    async ngOnInit(): Promise<void> {
        await this.loadComponent();
    }

    async loadComponent(): Promise<void> {
        const { TestComponent, TestModule } = await import('./test');
        const moduleFactory = await this.loadModuleFactory(TestModule);
        const moduleRef = moduleFactory.create(this.injector);
        const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(TestComponent);
        this.containerRef.createComponent(factory);
    }

    private async loadModuleFactory(moduleFactory: Type<TestModule>): Promise<NgModuleFactory<TestModule>> {
        if (moduleFactory instanceof NgModuleFactory) {
            return moduleFactory;
        } else {
            return await this.compiler.compileModuleAsync(moduleFactory);
        }
    }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

The app.module.ts is included here for completeness.

After compiling all these code snippets, the text within the p tags is not being displayed.

Answer №1

It turns out that the reason why I wasn't seeing the expected result of my countdown was due to a rxjs issue. I'm puzzled as to why my initial approach didn't yield the desired outcome:

from([...Array(10).keys()].reverse()).pipe(delay(1000));

To me, this seemed like a logical way to create a countdown. After searching on stackoverflow, I discovered alternative methods to achieve the correct functionality. Like this one:

const timer = 10; 
interval(1000).pipe(take(timer), map(time => (timer - 1) - time));

Answer №2

After reviewing your original example, I came up with a code snippet that might be helpful:

  numbers$: Observable<number> = from(
    Array.from(Array(10).keys()).reverse()
  ).pipe(
    concatMap(n => of(n).pipe(delay(1000))),
    tap(console.log)
  );

Here are a few points to consider:

In TypeScript, I had to avoid using the spread operator because of this issue: https://github.com/Microsoft/TypeScript/issues/18247

The delay operator delays the start of each emission, not the interval between emissions.

By turning each number into an Observable, delaying it, and then using concatMap to wait, the count down effect is achieved.

In terms of readability, the alternative method you mentioned appears clearer:

  numbers$ = interval(1000).pipe(
    take(10),
    map(time => 9 - time)
  );

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

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 ...

The functionality of ZoneAwarePromise has been modified within the Meteor framework

After updating to the latest Angular 2.0.1 release on Meteor 1.4.1.1, I'm facing an error that says: Zone.js has detected that ZoneAwarePromise (window|global).Promise has been overwritten I've attempted running meteor update and meteor reset, b ...

What steps do I need to take to ensure that the external API proxy for Angular 8 functions properly, without automatically defaulting to

In my current project, I'm attempting to set up a development Angular application to streamline the process of writing and testing services for our main NativeScript app in production. One of the challenges I've encountered is dealing with the C ...

Ways of invoking a component method from a service in Angular 2

I have a concept for creating a unique service that is capable of interacting with one specific component. In my application, all other components should have the ability to call upon this service and then have it interact with the designated component in ...

Creating a mock instance of a class and a function within that class using Jest

I am currently testing a class called ToTest.ts, which creates an instance of another class named Irrelevant.ts and calls a method on it called doSomething. // ToTest.ts const irrelevant = new Irrelevant(); export default class ToTest { // ... some impl ...

Encountering issues with Angular2 forms while working with JavaScriptServices/Universal

My Angular2 app was built using the JavaScriptServices starter from GitHub. The issue I'm encountering is a runtime error when I include a form in a component. Both FormsModule and ReactiveFormsModule are being imported. This is how my form is stru ...

I need help understanding how to access the map[#var] when using *ngFor="var of list"

If I define a local variable using *ngFor in my template, how can I utilize it as a key to access a map? The template code shown below leads to a Parser Error: Unexpected token # at column 12 in ... <li *ngFor="#item of my_list"> <div [class]="m ...

Converting a text file to JSON in TypeScript

I am currently working with a file that looks like this: id,code,name 1,PRT,Print 2,RFSH,Refresh 3,DEL,Delete My task is to reformat the file as shown below: [ {"id":1,"code":"PRT","name":"Print"}, {" ...

Troubleshooting problem with Angular HttpClient when invoking Bing Maps Locations REST APIs

Currently, I have successfully implemented a Bing Maps call using Angular 4 Http service: this.http.get("{absolute URL of Bing Maps REST Locations, with options and key}") Now, I am trying to transition this call to use the newer HttpClient service in An ...

Having trouble organizing the date strings in the material table column

In my Angular application, I have a material table with multiple columns that I am sorting using matSort. While I can successfully sort the last two columns in ascending or descending order, I am facing an issue with the first column which contains date va ...

Request with missing authentication header in Swagger OpenAPI 3.0

When generating the swagger.json using tsoa for TypeScript, I encountered an issue. Even after adding an access token to the authorize menu in Swagger and making a request to one of my endpoints, the x-access-token header is missing from the request. What ...

update the element that acts as the divider in a web address (Angular)

Is it possible to modify the separator character used when obtaining the URL parameters with route.queryParams.subscribe? Currently, Object.keys(params) separates the parameters using "&" as the separator. Is there a way to change this to use a differe ...

Unable to retrieve data using Http Get in Angular 2

In my service.ts class, I have the following method: getData(username:string, password:string) { let params: URLSearchParams = new URLSearchParams(); params.set('username', username); params.set('password', password); ...

Do you know of any resources that provide tutorials on utilizing Epics within Redux Observables?

I've searched extensively for a comprehensive tutorial on epics, but haven't found one yet. const pingEpic = action$ => action$.filter(action => action.type === 'PING') .delay(1000) // Wait asynchronously for 1000ms before ...

What is the best way to depict object key replacements within a Typescript definition?

I currently have these types: type PossibleKeys = number | string | symbol; type ValueOf<T extends object> = T[keyof T]; type ReplaceKeys<T extends Record<PossibleKeys, any>, U extends Partial<Record<keyof T, PossibleKeys>> = ...

Tips for enhancing the appearance of the dropdown scrollbar in PrimeNG

Just started exploring Angular and I've been struggling to customize the scrollbar on a PrimeNG dropdown. Does anyone have any tips or tricks to achieve this? Here is the HTML code: <p-autoComplete placeholder="- Select -" (onSelect)="onSelect(dh ...

The issue arises when the mat-panel-description is not properly aligned with the shrinking panel

Within my HTML, I have a mat-panel with a description that is populated by a variable from the TS file. However, I am encountering an issue where if I shrink the panel and the description is too long, the text is cut off at the top and bottom. Is there a w ...

Effectively generating observables that extract a designated element from a collection of observables

Within my application, I am utilizing a RxJS subject to broadcast changes within a collection. Each time the collection is updated, the subject emits the new contents as an array format. let collectionSubject = new Rx.BehaviourSubject(); collectionSubjec ...

Refresh the context whenever the state object changes

Within my application, I am utilizing a PageContext to maintain the state of various User objects stored as an array. Each User object includes a ScheduledPost object that undergoes changes when a user adds a new post. My challenge lies in figuring out how ...

The 'xxx' type does not have an index signature, so the element is implicitly assigned an 'any' type

I'm currently facing an issue with TypeScript. The error message I'm encountering is related to the following section of code: The Interface: export default interface IUser { username: string; email?: string; isActive: boolean; group: s ...