web-dev-qa-db-ja.com

Angular 2の複数のコンポーネントでグローバルに値を永続化してアクセスする

ユーザー名などのいくつかの構成変数を保存できる設定ページがあります。ユーザーは、そのページのユーザー名を変更することもできます。しかし、別のコンポーネント(ページ)に移動して戻ると、ユーザー名は保存されません。

他のコンポーネントにもユーザー名を表示したいと思います。それ、どうやったら出来るの?グローバル変数で?

構造:-アプリ-app.ts(メイン)-setting.ts-someOtherComponent.ts

17
user5605068

必要なのは、変数の状態を維持するサービスです。その後、そのサービスを任意のコンポーネントに注入して、その変数を設定または取得できます。

次に例を示します(/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 ;
    }
}

そのサービスをアプリのbootstrap=関数(アプリのメインコンポーネントファイルにあるか、物事のやり方によっては別のファイルにあるかもしれません)。

メインアプリファイル(/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

COMMON_DIRECTIVESおよびその他のすべてのcap項目は配列にある必要はありませんが、それらは通常、作成する各コンポーネントで構成する必要がないようにするために含まれています。

次に、このようなコンポーネント(/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();
    }
}

また、ユーザーがアプリケーションを次に入力したときにアクセスできるように、値をどこか(半)永続的に保存するようにサービスに追加することもできます。

33
Michael Oryl

これはあなたの現在の状況ではやり過ぎかもしれませんが、アプリケーションをより大きなものに成長させようとするなら、これは将来の多くのトラブルを救うかもしれません。とはいえ、Angular Reduxのようなストアは本当に強力な組み合わせです。私の提案は_ngrx/store_モジュールを使用することです。

AngularアプリケーションのRxJSによる状態管理、Reduxに触発

出典: _ngrx/store_ GitHubリポジトリ

以下のコードは、_ngrx/store_をアプリケーションに統合するために従う必要のあるすべての手順の概要を示しています。簡潔にするために、既存のコードと、追加する必要があるすべてのインポートを省略しました。

モジュールをインストールします

_npm install --save ngrx/core ngrx/store
_

レデューサーの作成

_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;
    }
};
_

StoreModuleをインポートします

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

Settings Reducerのインターフェイスの作成

_export interface SettingsStore {
    username: string;
}
_

ストアインターフェースの作成

_export interface AppStore {
    settings: SettingsStore;
}
_

設定サービスの作成

_@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 });
    }

}
_

コンポーネントコントローラーの設定

_@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' });
    }

}
_

設定コンポーネントテンプレート

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

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

上記のセットアップを使用すると、SettingsServiceをコンポーネントに挿入し、そのテンプレートに値を表示できます。

this.settingsService.update({ ... });を使用して、任意のコンポーネントからstoreの値を更新することもできます。変更は、その値を使用しているすべての場所に反映されます。 asyncパイプまたは.subscribe()を介した別のサービス。

9
Vladimir Zdenek

必要な場所に挿入できるように、これにサービスが必要です。

@Injectable()
export class GlobalService{
  private value;

  constructor(){

  }

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

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

すべてのモジュールでこのサービスを提供できますが、複数のインスタンスを取得する可能性があります。したがって、サービスをシングルトンにします。

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

forRootでのみAppModuleメソッドを呼び出す必要があります。

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

私は同じ問題を抱えています。ほとんどのコンポーネントには、loggedInUser情報が必要です。例:ユーザー名、優先言語、ユーザーの優先タイムゾーンなど。

そのため、ユーザーがログインすると、次のことができます。

すべての情報がJWTトークンまたは/ me RESTful APIにある場合、サービスの1つでトークンをデコードできます。

例: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);
  }

}

この変数をリッスンしている3つのパブリックBehaviorサブジェクトを保持している場合、値が変更されると値を取得します。 rxjsプログラミングスタイルを確認してください。

そして今、この変数が必要な場所ならどこでも、そのコンポーネントからサブスクライブするだけです。

例えば:NavComponentは間違いなくユーザー名を必要とします。したがって、コードは以下のようになります。

export class NavComponent implements OnInit {

    public username: string;

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

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

したがって、 リアクティブプログラミング は、依存関係変数を処理するためのより良いソリューションを提供します。

Localstorageに値を保存し、コンストラクターで値を取得するため、ページがハードリフレッシュされた場合にも上記の問題が解決します。

注意:ログインしたユーザーデータはJWTトークンからのものであると想定されます。データがRestful Apiからのものである場合は、同じservice(UserHttpService)も使用できます。例:api/me

2
virsha

Vladimirはngrxについて正しく言及しました。

必要な状態(値)をSubject/BehaviourSubjectおよびObservableからsetおよびgetのサービスを使用します。

ここで説明しました Angular 2のObservableとSubjectを使用したサービスとの複数のコンポーネント間の通信parent-childまたはchild-parentを持たない複数のコンポーネントで値を共有する方法関係とwithout外部ライブラリをngrxストアとしてインポートします。

2
DrNio