user2741109
user2741109

Reputation: 121

angular 2 – change interface in global service through component and update changed interface in another

I am playing around with angular 2. I want to build a global service that holds an interface. This interface can be changed through a HeaderComponent. After the user changed the interface through the HeaderComponent the interface has also been changed in another ChildComponent. Therefor I worked with this answer found on stack overflow.

Let's say I have an interface and 2 different classes. Each of these classes are holding a different type of the interface.

MyInterface

export interface MyInterface {
    key: string,
}

ClassA

import { MyInterface } from './interfaces/my-interface';
export class ClassA {
    type = "A";
    _interface: MyInterface = {
        key: "value1",
    };
}

ClassB

import { MyInterface } from './interfaces/my-interface';
export class ClassB {
    type = "B";
    _interface: MyInterface = {
        key: "value2",
    };
}

A global service is implementing this interface.

GlobalService

import { Injectable, EventEmitter }    from '@angular/core';

import { ClassA } from './classes/class-a';
import { ClassB } from './classes/class-b';
import { MyInterface } from './interfaces/my-interface';

@Injectable()
export class GlobalService {

    public interfaceChanged: EventEmitter<MyInterface>;
    _interface: MyInterface;

    interfaceList: string[] = [];

    interfaces = [
        new ClassA(),
        new ClassB()
    ];

    selectedInterface: string;

    constructor() {
        this.interfaceChanged = new EventEmitter<MyInterface>();
        for (var i = 0; i < this.interfaces.length; i++)
            this.interfaceList.push(this.interfaces[i].type);
        this.changeInterface(this.interfaceList[0]);
    }

    changeInterface(_interface: string): void {
        if (this.interfaceList.includes(_interface)) {
            this.selectedInterface = _interface;
            for (var i = 0; i < this.interfaces.length; i++) {
                if (this.interfaces[i].type == this.selectedInterface) {
                    this._interface = this.interfaces[i]._interface;
                    this.interfaceChanged.emit(this.interfaces[i]._interface);
                }
            }
        }
    }
}

Now the HeaderComponent which is implemented as directive in app.component.ts

app.component.ts

import { HeaderDirective } from './directives/header';
import { FooterDirective } from './directives/footer';

@Component({
  selector: 'my-app',
  template: `
    <my-header></my-header>
    <div class="container">
      <router-outlet></router-outlet>
    </div>
    <my-footer></my-footer>
  `,
  styleUrls: [ ],
  directives: [HeaderDirective, FooterDirective, ROUTER_DIRECTIVES]
})
export class AppComponent { }

is able to change the interface through a select field:

import { Component } from '@angular/core';
import { MyInterface } from './interfaces/my-interface';
import { LanguageService } from './services/global-service';

@Component({
    selector: 'my-header',
    template: `
      <select (change)="change($event.target.value)">
        <option *ngFor=" let _interface of interfaceList ">{{ _interface }}</option>
      </select>
    `,
})
export class HeaderComponent {

    selectedInterface: string;
    interfaceList: string[];
    _interface: MyInterface;

    constructor(private globalService: GlobalService) {
        this.selectedInterface = this.globalService.selectedInterface;
        this.interfaceList = this.globalService.interfaceList;
        this._interface = this.globalService._interface;
    }

    change(_interface: string) {
        this.globalService.changeInterface(_interface);
    }
}

So far so good. After I changed the interface through my HeaderComponent I want that the interface also be changed in another ChildComponent that is going to be displayed through <router-outlet></router-outlet>

import { Component, EventEmitter } from '@angular/core';
import { GlobalService } from './services/global-service';
import { MyInterface } from './interfaces/my-interface';

@Component({
  selector: 'my-child',
  template: `
    <p>Test: {{ _interface.key }}</p>
  `,
})
export class ChildComponent {

    private _interface: MyInterface;

    constructor(private globalService: GlobalService) {

        this.globalService.interfaceChanged
                          .toPromise()
                          .then(_interface => {
                              this.changeLanguage(_interface);
                          })
                          .catch(err => {
                              console.log(err);
                          });
    }

    changeInterface(_interface: MyInterface) {
        this._interface = _interface;
    }
}

The problem is that the change of the interface through the HeaderComponent is working but the interface doesn't change for the ChildComponent. The changeInterface(interface: MyInterface) function in my ChildComponent isn't even called. Here the user is working with:

...
constructor(globalService: GlobalService) {
    globalService.interfaceChanged.subscribe(_interface => this.changeInterface(_interface));
}
...

for the ChildComponent. But if I do so there is an error within my sublime editor: "Parameter 'interface' implicitly has an 'any' type." So what am I doing wrong? What am I missing here?

Here you can see it on Plunker.

Upvotes: 2

Views: 1999

Answers (1)

Harry Ninh
Harry Ninh

Reputation: 16758

The error in your editor

"Parameter 'interface' implicitly has an 'any' type."

is because you have strict TypeScript rule that prevents tsc from compiling your code. Either change the config by going to tsconfig.json and turn off noImplicitAny flag:

"noImplicitAny": false

Or add type to your interface in your subscribe callback:

globalService.interfaceChanged.subscribe((_interface: MyInterface) 
  => this.changeInterface(_interface));

Upvotes: 2

Related Questions