Сохранение и доступ к значениям глобально по нескольким компонентам в 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
Ответ 6
если вы ищете простое решение, то угловое постоянство может быть отличным решением