web-dev-qa-db-fra.com

Persistance et accès aux valeurs globales dans plusieurs composants dans Angular 2

J'ai une page de paramètres où les utilisateurs peuvent enregistrer certaines variables de configuration, telles qu'un nom d'utilisateur. L'utilisateur peut également changer le nom d'utilisateur sur cette page. Mais lorsque je vais dans un autre composant (page) et retour, le nom d'utilisateur n'est pas enregistré.

Je veux aussi montrer le nom d'utilisateur dans les autres composants. Comment je fais ça? Avec une variable globale?

Structure: - App - app.ts (main) - setting.ts - someOtherComponent.ts

17
user5605068

Ce dont vous avez besoin, c'est d'un service pour maintenir l'état de vos variables. Vous pourrez alors injecter ce service dans n’importe quel composant pour définir ou obtenir cette variable.

Voici un exemple (/services/my.service.ts):

import {Injectable} from "angular2/core";

@Injectable()
export class MyService {
    private myValue;

    constructor() {}

    setValue(val) {
        this.myValue = val;
    }

    getValue() {
        return this.myValue ;
    }
}

Vous voudrez probablement mettre ce service dans le tableau des fournisseurs de la fonction d'amorçage de votre application (qui peut se trouver dans le fichier de composant de votre application principale ou dans un fichier séparé, en fonction de la manière dont vous aimez faire les choses).

Dans votre fichier d'application principal (/app.ts):

import {MyService} from './services/my.service';
bootstrap(App, [MyService, COMMON_DIRECTIVES, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, HTTP_PROVIDERS]); // directives added here are available to all children

Vous n'avez pas besoin de COMMON_DIRECTIVES ni des autres éléments tout en majuscules du tableau, mais ceux-ci sont généralement inclus simplement pour que vous n'ayez pas à les configurer dans chaque composant que vous écrivez.

Ensuite, vous accéderiez au service à partir d'un composant tel que (/components/some-component.ts):

import {MyService} from '../services/my.service';

@Component({
    selector: 'some-component',
    template: `
<div>MyValue: {{val}}</div> 
`
})
export class SomeComponent {
    constructor(private myService:MyService) {
    }

    get val() {
        return this.myService.getValue();
    }
}

Vous pouvez également vouloir ajouter au service afin qu'il enregistre la valeur dans un emplacement (semi) permanent afin de pouvoir y accéder la prochaine fois que l'utilisateur entrera dans l'application.

33
Michael Oryl

Votre situation actuelle est peut-être exagérée, mais si vous envisagez d'agrandir votre application, vous éviterez de nombreux problèmes. Cela dit, je pense qu'Annular avec un magasin de type Redux est une combinaison très puissante. Ma suggestion est d'utiliser le module ngrx/store.

Gestion des états basée sur RxJS pour les applications angulaires, inspirée de Redux

Source: ngrx/store GitHub repo

Le code ci-dessous doit décrire toutes les étapes à suivre pour intégrer le ngrx/store à votre application. Par souci de brièveté, j'ai omis tout code existant que vous pourriez avoir et toutes les importations que vous devrez ajouter.

Installer le module

npm install --save ngrx/core ngrx/store

Créer un réducteur

export const SETTINGS_UPDATE = 'SETTINGS_UPDATE';

const initialState = {
    username: ''
};

export function settingsReducer(state: Object = initialState, action: Action) {
    switch (action.type) {
        case SETTINGS_UPDATE:
            return Object.assign({}, state, action.payload);
        default:
            return state;
    }
};

Importer le StoreModule

@NgModule({
    imports: [
        // your other imports
        StoreModule.provideStore({
            settings: settingsReducer
        })
    ]
})
export class AppModule {
    //
}

Créer une interface pour le réducteur de paramètres

export interface SettingsStore {
    username: string;
}

Créer une interface de magasin

export interface AppStore {
    settings: SettingsStore;
}

Créer un service de paramètres

@Injectable()
export class SettingsService {

    settings$: Observable<SettingsStore>;

    constructor(private store: Store<AppStore>) {
        this.settings$ = this.store.select('settings');
    }

    public update(payload) {
        this.store.dispatch({ type: SETTINGS_UPDATE, payload });
    }

}

Paramètres Component Controller

@Component({
    ...
})
export class SettingsComponent implements OnInit {

    settings$: Observable<SettingsStore>;

    constructor(private settingsService: SettingsService) {
        //
    }

    ngOnInit() {
        this.settings$ = this.settingsService.settings$;
    }

    public updateUsername() {
        this.settingsService.update({ username: 'newUsername' });
    }

}

Modèle de composant de paramètres

<p *ngIf="(settings$ | async)?.username">
    <strong>Username:</strong>
    <span>{{ (settings$ | async)?.username }}</span>
</p>

<button (click)="updateUsername()">Update Username</button>

Avec la configuration décrite ci-dessus, vous pouvez ensuite injecter la SettingsService dans n’importe quel composant et afficher la valeur dans son modèle.

De la même manière, vous pouvez également mettre à jour la valeur de store à partir de n’importe quel composant en utilisant this.settingsService.update({ ... });. Le changement sera répercuté sur tous les emplacements qui utilisent cette valeur - qu’il s’agisse d’un composant via un canal async ou d’un autre service via .subscribe().

9
Vladimir Zdenek

Vladimir a correctement mentionné à propos de ngrx.

J'utiliserais un service avec SubjectBehaviourSubject et Observable à set et get l'état (valeurs) dont j'ai besoin. 

Nous avons discuté ici Communication entre plusieurs composants avec un service en utilisant Observable et Subject dans Angular 2 comment partager des valeurs dans plusieurs composants sans relation parent-child ou child-parent et _/sans bibliothèque en tant que magasin ngrx

2
DrNio

Vous auriez besoin d’un service pour pouvoir vous injecter partout où vous en avez besoin:

@Injectable()
export class GlobalService{
  private value;

  constructor(){

  }

  public setValue(value){
    this.value = value;
  }

  public getValue(){
    return this.value;
  }
}

Vous pouvez fournir ce service dans tous vos modules, mais il est possible que vous obteniez plusieurs instances. Par conséquent, nous ferons du service un singleton.

@NgModule({
  providers: [ /* DONT ADD THE SERVICE HERE */ ]
})
class GlobalModule {
  static forRoot() {
    return {
      ngModule: GlobalModule,
      providers: [ GlobalService ]
    }
  }
}

Vous ne devez appeler la méthode forRoot que dans votre AppModule:

@NgModule({
  imports: [GlobalModule.forRoot()]
})
class AppModule {}
2
Robin Dijkhof

J'ai le même problème, la plupart des composants ont besoin d'informations logInUser ..__ Par exemple: nom d'utilisateur, langue préférée, fuseau horaire préféré de l'utilisateur, etc.

Donc pour cela, une fois que l'utilisateur se connecte, on peut faire ce qui suit:

Si toutes les informations sont dans JWT Token ou/me RESTful Api, vous pouvez alors décoder le jeton dans l'un des services. 

par exemple: user.service.ts 

@Injectable()
export class UserService {

  public username = new BehaviorSubject<string>('');
  public preferredLanguage = new BehaviorSubject<string>('');
  public preferredTimezone = new BehaviorSubject<string>('');

  constructor(
    private router: Router,
    private jwtTokenService: JwtTokenService
  ) {
    let token: string = localStorage.getItem('token'); // handled for page hard refresh event
    if (token != null) {
      this.decode(token);
    }
  }

  private decode(token: string) {
    let jwt: any = this.jwtTokenService.decodeToken(token);
    this.username.next(jwt['sub']);
    this.preferredLanguage.next(jwt['preferredLanguage']);
    this.preferredTimezone.next(jwt['preferredTimezone']);
  }

  public setToken(token: any) {
    localStorage.setItem('auth_token', token);
    this.decode(token);
  }

}

qui détient trois sujets de comportement publics, quiconque écoute cette variable obtiendra la valeur si elle change. Veuillez vérifier le style de programmation de rxjs.

et maintenant, partout où cette variable est requise, il suffit de s’abonner à ce composant.

Par exemple: NavComponent aura certainement besoin d'un nom d'utilisateur. le code va donc ressembler à ceci ci-dessous:

export class NavComponent implements OnInit {

    public username: string;

    constructor(
        private userService: UserService,
        private router: Router) {
    }        

    ngOnInit() {
        this.userService.username.subscribe(data => {
          this.username = data;
        });        
    }
}

Par conséquent, programmation réactive donne une meilleure solution pour gérer la variable de dépendance.

Ce qui précède résout également le problème si l'actualisation de la page est difficile, car je stocke la valeur dans localstorage et la récupère dans le constructeur.

Remarque: il est supposé que les données utilisateur enregistrées proviennent de jetons JWT. Nous pouvons également utiliser le même service (UserHttpService) si les données proviennent de Restful Api. Par exemple: api/moi

1
virsha