web-dev-qa-db-fra.com

Angular 2: extension des services et paramètres de passage

J'ai du mal à comprendre comment étendre les services dans Angular2. 

J'ai un service qui se connecte à Firebase et effectue toutes sortes de tâches courantes (obtenir, définir, mettre à jour, liste, etc.) et au lieu de le réécrire pour mes composants spéciaux, j'ai simplement essayé de l'étendre. 

L'idée était que je pouvais passer juste la nouvelle partie du chemin mais cela jette une erreur: 

Cannot resolve all parameters for 'FirebaseService'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'FirebaseService' is decorated with Injectable.

Le problème est dans le constructeur et mon manque de OOP cerveaux. Je peux transmettre d'autres services ou fournisseurs à mon service, mais je ne peux plus transmettre de paramètres de chaîne simples à moins que quelque chose me manque. J'ai essayé de définir des propriétés, mais je ne pense pas que le contexte est correct.

Je pensais que c'était un problème avec le @Injectable mais je ne suis pas sûr. 

Voici une version simplifiée de ce que j'ai d'abord essayé:

MISE À JOUR POUR INCLURE DES LIENS DE PLUNKER:

Plunker pour passer avec des paramètres

Plunker pour passer avec constructeur

@Injectable()
export class FirebaseService {
  rootPath:string = "https://mysite.firebase.com/";
  childPath:string;
  pathToUse: string;
  constructor() {
    if(this.childPath){
        this.pathToUse = this.rootPath + this.childPath;
    }else{
        this.pathToUse = this.rootPath;
    }
    console.log(this.pathToUse);
  }
}
//The in project_service.ts
@Injectable()
export class ProjectService extends FirebaseService{
    childPath:string = "projects";
    constructor(){
        super();
    }
}

Je m'attendais à avoir la ligne "projets" attachée. Ça ne se répète pas. 

Alors j'ai essayé de passer par le constructeur:

@Injectable()
export class FirebaseService {
  rootPath:string = "https://mysite.firebase.com";
  pathToUse: string;
  constructor(childPath:string) {
    if(childPath){
        this.pathToUse = this.rootPath + childPath;
    }else{
        this.pathToUse = this.rootPath;
    }
    console.log(this.pathToUse);
  }
}
//The in project_service.ts
@Injectable()
export class ProjectService extends FirebaseService{
    constructor(){
        super('projects');
    }
}

Ce qui fait tout exploser. J'ai un moyen de le contourner, mais cela me semble être un hack total. 

Quelle est la bonne façon de passer le paramètre "projects" à la classe parente?

5
Dennis Smolek

Donc, après quelques bons travaux de CH Buckingham, j'ai décidé qu'il était impossible de le faire "de manière typique". 

Angular2 prend simplement en charge la fonction constructor () avec l'injecteur. Ce qui fonctionne, cependant, est de créer une autre fonction "init" à laquelle vous pouvez ensuite passer des paramètres. 

@Injectable()
export class ParentService {
  root:string = "This content comes from: ";
  myString:string = "The Parent";
  resultString:string;
  constructor(){
    this.init();
  }
  init() {
    this.resultString = this.root + this.myString;
  }
}


@Injectable()
export class ChildService extends ParentService {
  constructor(){
    super();
  }
  init() {
    this.myString = "The Child";
    super.init();
  }
}

De cette façon, vous pouvez définir des valeurs sur l'objet enfant ou les transmettre au parent.

Plunker de cela en action

8
Dennis Smolek

Voici un autre moyen d'étendre un service dans angular2 qui fonctionne avec le DI. Le service enfant ne peut pas utiliser le même nom de paramètre pour les services injectables utilisés par le parent, d'où la raison du nom de paramètre anotherHttp.

Le service parent:

@Injectable()
export class GenericStore {
   ....
   constructor(public config:GenericConfig,private http:Http) {
        ....
   }
   ....
}

Le service enfants:

const DEFAULT_CONFIG:GenericConfig = { ... };
@Injectable()
export class ConfigStore extends GenericStore {
   ....
   constructor(private anotherHttp:Http) {
        super(DEFAULT_CONFIG,anotherHttp);
   }
   ....
}
5
Aaron Boteler

Mise à jour: si vous souhaitez utiliser l'interface de service standard sans instanciation manuelle et transmettre des valeurs simples au constructeur, la documentation vous recommande de configurer une usine, qui prend une quantité de code presque stupide: Dependency Injection (faites défiler jusqu'à Fournisseurs d’usine).

//this is all Google Example Code
constructor(private logger: Logger, private useCoolFeature: boolean) {}
let heroServiceFactory = (logger: Logger, userService: UserService) => {
  return new HeroService(logger, userService.user.isSpecial);
}
let heroServiceDefinition = {
   useFactory: heroServiceFactory,
   deps: [Logger, UserService]
};
let heroServiceProvider = provide(HeroService, heroServiceDefinition);
bootstrap(AppComponent, [heroServiceProvider, Logger, UserService]);

Ce code ci-dessous fonctionne correctement, mais n'utilise pas le système du fournisseur pour les services:

//things.service.ts
@Injectable()
export class ThingService {
  public myString: string = "base";
  constructor(str: string) {
    this.myString = str;
  }
}

@Injectable()
export class Thing2Service extends ThingService {
  constructor(){
    super('I AM CHILD');
  }
}

//app.component.ts
public thingService: ThingService = new ThingService("I AM PARENT");
public thing2Service: Thing2Service = new Thing2Service();

ThingService.myString === "I AM PARENT"; //true
Thing2Service.myString === "I AM CHILD"; //true
2
Cooper Buckingham

La meilleure façon pour IMO consiste à utiliser une propriété abstraite. De cette façon, la classe d'extension sera obligée de fournir la valeur requise. 

abstract class GenericStore{

  abstract configVal: string;

  constructor(){}

  someMethod(){
    console.log('The config value is:', this.configVal);
  }
}

class UserStore extends GenericStore{

  configVal = 'A config value. TypeScript will yell if not provided.'; 

  constructor(){
    super();
  }
}
1
Scottmas