Thomas
Thomas

Reputation: 457

Asynchronously updating inputs in Angular 2 Components

Is there a way to have an async callback feed an @Input of a Component in Angular 2? (I'm specifically using RC7 right now.)

My goal is to have a hierarchy of wrapper components that serve as interfaces to the service at that level in the hierarchy. The idea is one could use the parent component's interfaces to provide a model to the child component, and so on. In other words, the parent component would provide a getChild(ID) which would then map to an input on the child component to set its model, all in the template:

<parent-comp #parent>
    <child-comp 
        *ngFor="let id of parent.childIds;" 
        [model]="parent.getChild(id)">
        <!-- And so on, with the child's children, etc. -->
    </child-comp>
</parent-comp>

The parent component's method wraps a service method which returns a promise (at this point). So the contents of the parent component's getChild() method is the issue since I need to return the instance later.

One solution I've considered is reworking these methods to pass an instance of the model to populate.

// In the parent component
public getChild(id: string): Child {
    let child = new Child();
    this.service.getChild(id, child);
    return child;
}

// In the parent service
public getChild(id: string, model: Child): Promise<Child> {
    this.http.get(some_url).toPromise().then(res => model.update(res));
}

Are there better ways of handling this construct, so that a parent component method can provide a model to the child, and so-on?

Upvotes: 0

Views: 313

Answers (1)

Thomas
Thomas

Reputation: 457

Note: I've since moved on to Angular 2.1.

One solution I found for this was to leverage the dependency injection pattern of services. For example, in my module I had only one singleton service; everything else was a hierarchy off that service. So at the NgModule, I provided that service:

@NgModule({ provides: SuperService })
export class MyModule;

For the first-tier service that would rely on that service (i.e., this service would be a child of SuperService), it would be defined with a reference to this SuperService from its constructor:

@Injectable()
export class FirstTierService (private superService: SuperService) { /** */ }

The corresponding component then calls out both the SuperService and a new instance of its own FirstTierService:

@Component({
    providers: [FirstTierService]
})
export class FirstTierComponent {
    constructor (
        private ftService: FirstTierService,
        @Host() private superService: SuperService) { /** */ }
}

This ensured that the FirstTeirComponent would get its SuperService from the host Component's instance (some parent Component in the DOM). Dropping the decorator will let the dependency injection drop the DOM enforcement that the service be found on the parent component rather than possibly on this element (I may be misunderstanding the docs on that).

Children are established with a similar pattern. The child service would reference the parent in its constructor, and the child component would put its own service in the provides of the Component definition as well as reference both services in its constructor.

@Injectable()
export class SecondTierService (private ftService: FirstTierService) { /** */ }

@Component({
    providers: [SecondTierService]
})
export class SecondTierComponent {
    constructor (
        private stService: SecondTierService,
        @Host() private ftService: FirstTierService) { /** */ }
}

As for the HTML, I was misunderstanding the # reference:

<parent #parent>
    <child *ngFor="let child of parent.ftService.children">
    </child>
</parent>

(Assuming of course the first-tier parent service member variable was called ftService and had an array member called children.)

Upvotes: 1

Related Questions