Сохранение и доступ к значениям глобально по нескольким компонентам в Angular 2

У меня есть страница настроек, где пользователи могут сохранять некоторые переменные конфигурации, такие как имя пользователя. Пользователь также может изменить имя пользователя на этой странице. Но когда я перехожу на другой компонент (страница) и обратно, имя пользователя не сохраняется.

Я также хочу показать имя пользователя в других компонентах. Как мне это сделать? С глобальной переменной?

Структура: - Приложение   - app.ts(main)   - setting.ts   - someOtherComponent.ts

Ответы

Ответ 1

Вам нужна служба для поддержания состояния ваших переменных. Затем вы сможете внедрить эту услугу в любой компонент, чтобы установить или получить эту переменную.

Вот пример (/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 ;
    }
}

Вероятно, вы захотите разместить эту службу в массиве поставщиков функции загрузки приложения (которая может находиться в главном файле компонента приложения или в отдельном файле в зависимости от того, как вам нравится что-то делать).

В главном файле приложения (/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, а остальные все кепки в массиве, но они обычно включены только для того, чтобы сделать это так, что вам не нужно настраивать их в каждом компоненте, который вы пишете.

Затем вы получите доступ к сервису из этого компонента (/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();
    }
}

Вы также можете добавить в службу, чтобы сохранить значение где-то (полу) постоянным, чтобы его можно было получить при следующем входе пользователя в приложение.

Ответ 2

Это может быть излишним в вашей текущей ситуации, но если вы собираетесь превратить ваше приложение в большее, это может избавить вас от многих неприятностей в будущем. Тем не менее, я считаю Angular с Redux-подобным магазином действительно мощной комбинацией. Я предлагаю использовать ngrx/store.

Управление состоянием RxJS для приложений Angular, вдохновленное Redux

Источник: ngrx/store GitHub repo

Приведенный ниже код должен описать все шаги, которые вы должны выполнить, чтобы интегрировать 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 {
    //
}

Создать интерфейс для настройки параметров

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 в любой компонент и отобразить значение в его шаблоне.

Таким же образом вы также можете обновить значение store из любого компонента, используя this.settingsService.update({... }); и изменение будет отражено во всех местах, где используется это значение - будь то компонент через async канал или другой сервис через .subscribe().

Ответ 3

Вам понадобится сервис, чтобы вы могли вводить туда, где вам это нужно.:

@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 {}

Ответ 4

Владимир правильно упомянул о ngrx.

Я бы использовал сервис с Subject/BehaviourSubject и Observable до set и get состояние (значения), которое мне нужно.

Мы обсуждали здесь Связь между несколькими компонентами с помощью службы с помощью Observable и Subject в Angular 2, как вы можете делиться значениями в нескольких компонентах, которые не имеют parent - child или child - parent и без импортировать внешнюю библиотеку в качестве хранилища ngrx.

Ответ 5

У меня такая же проблема, большая часть компонента нуждается в информации loggedInUser. Например: имя пользователя, предпочтительный язык, предпочтительный часовой пояс для пользователя и т.д.

Итак, для этого, как только пользователь войдет в систему, мы можем сделать следующее:

Если вся информация находится в JWT Token или /me RESTful Api, тогда вы можете декодировать токен в одном из сервисов.

например: 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);
  }

}

который содержит три объекта общественного поведения, тот, кто слушает эту переменную, получит значение, если оно изменится. Проверьте стиль программирования 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, мы можем также использовать ту же службу (UserHttpService), если данные поступают из Restful Api. Например: api/me