web-dev-qa-db-fra.com

Angular 4: comment passer un argument à un service fourni dans un fichier de module

Je vous appelle pour vous aider sur la méthode à utiliser ici: j'ai testé beaucoup de choses pour résoudre mon problème et j'ai au moins 4 solutions de travail, mais je ne sais pas laquelle serait la meilleure et si certains d'entre eux ne fonctionnent que parce que j'ai eu d'autres effets secondaires et ne devraient pas être utilisés ...

Je n'ai rien trouvé dans la documentation qui m'indique une de ces solutions (ou une autre)

Premièrement, voici ma situation: je construis une application qui se connecte à une API pour les données et construit différentes sections, chacune contenant des données.

J'ai construit les sections du site pour qu'elles soient des modules, mais elles partagent un service nommé DataService (chacun de ces modules utilisera DataService pour obtenir et traiter les données).

My DataService a besoin d'un fichier de configuration avec un certain nombre d'options spécifiques à chaque section et stockées dans son propre dossier.

J'ai donc besoin de fournir DataService séparément pour chaque section, dans la section providers du fichier module.ts, et cela semblait être une bonne pratique d'éviter de copier DataService dans chaque module ...

Donc, l'architecture des fichiers serait:

--Dashboard module
----data.service.ts
----Section1 module
------Section1 config file
----Section2 module
------Section2 config file

J'ai essayé plusieurs choses qui semblent toutes fonctionner dans le fichier de module de section:

Solution 1: injection

(Je ne pouvais pas faire fonctionner l'injection sans guillemets, même si je ne l'avais vue nulle part)

Fichier de module:

import { DataService } from '../services/data.service';
import viewConfig from './view.config.json';

@NgModule({
    imports: [ ... ],
    declarations: [ ... ],
    providers: [
        { provide: 'VIEW_CONFIG', useValue: viewConfig },
        DataService,
    ]})
   export class Section1Module { }

Fichier de section:

@Injectable()
export class DataService {

    constructor(@Inject('VIEW_CONFIG') private viewConfig : any) {}

}

Solution 2: fournisseur d'usine

Ne fonctionne pas après le redémarrage du serveur (voir la mise à jour)}

Inspiré de https://angular.io/docs/ts/latest/guide/dependency-injection.html#!#factory-provider

Fichier de module:

import { DataService } from '../services/data.service';
import viewConfig from './view.config.json';

export let VIEW_CONFIG = new InjectionToken<any>('');
let dataServiceFactory = (configObject):DataService => {
    return new DataService(configObject)
}

@NgModule({
    imports: [ ... ],
    declarations: [ ... ],
    providers: [
        { provide: VIEW_CONFIG, useValue: viewConfig },
        {
            provide : DataService,
            useFactory : dataServiceFactory,
            deps : [VIEW_CONFIG]
        },
    ]})
   export class Section1Module { }

Fichier de section:

@Injectable()
export class DataService {

    constructor(private viewConfig : any) {}

}

Solution 3: fournisseur personnalisé avec instanciation directe

Ne fonctionne pas après le redémarrage du serveur (voir la mise à jour)}

Fichier de module:

import { DataService } from '../services/data.service';
import viewConfig from './view.config.json';

@NgModule({
    imports: [ ... ],
    declarations: [ ... ],
    providers: [
        { provide: DataService, useValue : new DataService(viewConfig) },
    ]})
   export class Section1Module { }

Fichier de section:

@Injectable()
export class DataService {

    constructor(private viewConfig : any) {}

}

Solution 4: fournisseur personnalisé avec usine

Ne fonctionne pas après le redémarrage du serveur (voir la mise à jour)}

Très similaire à la solution 3 ...

Fichier de module:

import { DataService } from '../services/data.service';
import viewConfig from './view.config.json';

let dataServiceFactory = (configObject) => { return new DataService(configObject) }

@NgModule({
    imports: [ ... ],
    declarations: [ ... ],
    providers: [
         { provide: DataService, useValue : dataServiceFactory(viewConfig) }
    ]})
   export class Section1Module { }

Fichier de section:

@Injectable()
export class DataService {

    constructor(private viewConfig : any) {}

}

Solution 5 Utilisation d'une usine exportée à partir du fichier de service ET et du jeton d'injection

Fichier de module:

export const VIEW_CONFIG = new InjectionToken<any>('');

 @NgModule({
    imports: [ ... ],
    declarations: [ ... ],
    providers: [
         { provide: VIEW_CONFIG, useValue: viewConfig },
         { provide : DataService,
           useFactory: dataServiceFactory,
           deps : [VIEW_CONFIG] }
    ]})
   export class Section1Module { }

Fichier de section:

@Injectable()
export class DataService {
    constructor(private viewConfig : any) {}
}

export function dataServiceFactory(configObject) {
    return new DataService(configObject);
}

Je suis ouvert à toute critique, idée, nouvelle piste de quelque nature que ce soit sur "Quelle serait la bonne solution?" ou même "est la bonne solution parmi ceux-ci?"

Merci beaucoup ! :RÉ


UPDATE À un moment donné, j'ai réalisé que mon Angular-CLI se comportait étrangement et que certaines de ces méthodes ne fonctionnaient pas si je tuais mon serveur local et faisais un "ng serve". Ce qui est encore plus étrange, c'est que ça ne marche pas juste après avoir tué le serveur local de la CLI et l'avoir redémarré, mais la prochaine fois que j'enregistre un fichier et que la CLI est recompilée, cela fonctionne très bien ...

Solution 1: fonctionne toujours

Solution 2/Solution 3: échec avec erreur:

Error encountered resolving symbol values statically. Calling function 
'DataService', function calls are not supported. Consider replacing the 
function or lambda with a reference to an exported function

Solution 4: échec avec erreur:

Error encountered resolving symbol values statically. Reference to a non-exported function

Solution ajoutée 5

7
Sophie Jobez

Mon approche d'utilisation des données de configuration de la vue est la suivante:

Mon fichier de configuration:

export const IBO_DETAILS_GENERAL = {
    iboCode: 'IBOsFormCodeField',
    iboGivenName: 'IBOsFormGivenNameField',
    iboFamilyName: 'IBOsFormFamilyNameField',
    iboEmail: 'IBOsFormEmailField',
};

export const IBO_DETAILS_ACCOUNT = [];

Mon composant de vue:

import { IBO_DETAILS_GENERAL } from './ibo-details-formfields';

@Component({
    selector: 'ibo-details-general',
    templateUrl: './ibo-details-general.component.html',
    styles: [],
})
export class IboDetailsGeneral extends FilterDataMappingManipulator implements OnInit, OnDestroy {
    // Initial constants,vectors and component mapping
    private formFields = IBO_DETAILS_GENERAL;

Ainsi, de cette façon, j'ai ma config dans formFields. Et je peux l'utiliser n'importe où dans la portée de ce composant. 

Je l'utilise actuellement pour générer des champs de formulaire dynamiques (entrée, sélection, etc.) en fonction des données que je reçois de mon appel API.

Mais vous pouvez utiliser cet objet (formFields dans mon cas) pour rechercher ce dont vous avez besoin et l'envoyer à votre service en tant que paramètre.

Aussi, bon travail pour mettre le service partagé à un niveau supérieur. Je crois que c'est la meilleure approche. Mais je ne l'ajouterais pas à providers sur chaque composant, je l'ajouterais au module du composant. Comme ça:

@NgModule({
    declarations: [
        ChildComp1,
        ChildComp2
    ],
    imports: [
    ],
    providers: [
        SharedService
    ]
})
export class SectionModule {
}

De cette façon, ChildComp1 et ChildComp2 auront accès à SharedService sans avoir à l'ajouter dans le code du composant. 

Cela fonctionne bien lorsque, par exemple, vous définissez une section de Utilisateur. À l'intérieur, par exemple, UsersModule vous déclarez dans providers votre UsersSharedService et ensuite dans declarations vous déclarez votre UsersLeftPanel et UsersRightPanel(exemples).

Mise à jour 1:

Exemple d'utilisation de config dans le service.

Imaginez que le service partagé utilise cette méthode:

getData() {
  // do Stuff
  let myData = {}; // Result of the stuff we did
  this.dataSubject.next(myData);
}

Dans votre composant, vous appelez ceci comme ceci:

this.myService.getData();

Droite?

Maintenant, rappelez-vous que notre config a été déclarée? Ajoutez-le à l'appel.

this.myService.getData(this.formFields);

Et à notre service:

   getData(currentConfig) {
      // do Stuff depending on currentConfig obj received as parameter
      let myData = {}; // Result of the stuff we did
      this.dataSubject.next(myData);
    }

De cette façon, vous appelez la méthode getData() de notre service partagé en transmettant différentes configurations. De cette façon, vous n'avez pas à inclure votre service dans plusieurs providers et vous n'avez pas à copier/coller la logique qui gère la configuration, vous l'avez dans votre service partagé et, par conséquent, à tous vos enfants. y avoir accès.

Mise à jour 2:

Suite à votre approche Solution 5, je pense que vous manquez multi: true.

Essaye ça:

export function dataServiceFactory(configObject) {
    return () => new DataService(configObject);
}

providers: [
     { provide: VIEW_CONFIG, useValue: viewConfig },
     { provide : DataService,
       useFactory: dataServiceFactory,
       deps : [VIEW_CONFIG],
       multi: true }
]})

La return dans la fonction exportée est la clé: return () =>.

Voici comment je l'ai dans mon projet:

export function initConfigFactory(userConfig: UserConfig) {
    return () => userConfig.getUsersConfig();
}

userConfig.getUsersConfig() est un appel de service qui obtient la configuration de l'utilisateur.

providers: [
    {
        provide: APP_INITIALIZER,
        useFactory: initConfigFactory,
        deps: [UserConfig],
        multi: true
    }
]

Ceci est très proche de votre Solution 5, essayez-le! 

1
SrAxi