#angular #rxjs
#angular #rxjs
Вопрос:
Помогите! Я потратил весь день и большую часть вчерашнего дня, пытаясь заставить observable работать в моем приложении Angular. У меня есть компонент, который используется для изменения пользовательских настроек / config для приложения, и я хочу иметь возможность уведомлять другие компоненты при изменении настроек. Чем больше я читаю, тем больше запутываюсь. Вот мои настройки-контейнерный код:
import { Component, OnInit, Input } from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import { Observable } from 'rxjs';
import { MatSelectModule, SELECT_ITEM_HEIGHT_EM } from '@angular/material/select';
import { AppSettings } from '../../shared/app-settings'
import { AppSettingsService } from '../../services/app-settings.service';
import { APPSETTINGS } from '../../shared/defaultSettings';
import { ThemeService } from '../../services/theme.service';
import { LogService } from 'src/app/services/app-log.service';
@Component({
selector: 'app-settings-container',
templateUrl: './settings-container.component.html',
styleUrls: ['./settings-container.component.scss']
})
export class SettingsContainerComponent implements OnInit {
className: string;
settings: AppSettings;
@Input()
defaultSettings = APPSETTINGS;
themes = [
{ value: 'DEFAULT-THEME', label: 'blue' },
{ value: 'LIGHT-THEME', label: 'light' },
{ value: 'NATURE-THEME', label: 'nature' },
{ value: 'BLACK-THEME', label: 'dark' }
];
wings = [
{ value: 'ALWG', label: 'Alabama Wing' },
{ value: 'WYWG', label: 'Wyoming Wing' },
];
@Input()
title: string;
constructor(private appSettingsService: AppSettingsService,
private logger: LogService,
private themeService: ThemeService) { }
ngOnInit() {
this.className = this.constructor.toString().match(/w /g)[1];
this.settings = this.appSettingsService.getSettings();
}
saveSettings( event ) {
this.logger.debug(this.className, 'saveSettings, settings:', this.settings);
this.appSettingsService.saveSettings(this.settings);
}
}
Все это работает нормально, и все мои обратные вызовы (удаленные для краткости) работают нормально. Служба настроек короткая и приятная, как показано ниже:
import { Input } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { BehaviorSubject } from 'rxjs';
import { AppSettings } from '../shared/app-settings';
import { APPSETTINGS } from "../shared/defaultSettings";
import { Inject, Injectable } from '@angular/core';
import { LOCAL_STORAGE, StorageService } from 'angular-webstorage-service';
import { LogService } from 'src/app/services/app-log.service';
@Injectable()
export class AppSettingsService {
@Input()
defaultSettings = APPSETTINGS;
settings: Observable<AppSettings>;
newSettings: AppSettings;
// Use of BehavioirSubject. This is where you post values into
private _settings$: BehaviorSubject<AppSettings> = new BehaviorSubject(APPSETTINGS)
// Settings observer. This is where you read from outside
settings$: Observable<AppSettings> = this._settings$.asObservable();
constructor(private logger: LogService,
@Inject(LOCAL_STORAGE) private storage: StorageService) {
this.newSettings = this.storage.get('TRACS3_SETTINGS');
if ( this.newSettings !== null ) {
this._settings$.next(this.newSettings);
} else {
this.storage.set('TRACS3_SETTINGS', APPSETTINGS);
this.newSettings = this.storage.get( 'TRACS3_SETTINGS');
console.log('about to change observable');
this._settings$.next( this.newSettings );
console.log('just changed observabe to ', this.newSettings);
}
}
public saveSettings(settings: AppSettings): void{
console.log('saving settings in service');
//this._settings$.post(settings)
this._settings$.next(settings);
this.storage.set('TRACS3_SETTINGS', settings);
}
public getSettings(): Observable<AppSettings> {
return this.storage.get('TRACS3_SETTINGS');
}
}
Настройки должным образом сохранены в локальном хранилище и считываются при запуске, как и должны. Однако чего я пытаюсь добиться, так это уведомления моего app.component при изменении настроек (заголовок панели навигации — одна из настроек). Но я бился головой о стену, пытаясь понять, как это сделать. Вот мое приложение.код компонента:
import { Component, OnInit } from '@angular/core';
import { MatIconRegistry } from "@angular/material/icon";
import { DomSanitizer } from "@angular/platform-browser";
import { Inject } from '@angular/core';
import { Observable } from 'rxjs';
import { AdalService } from 'adal-angular4';
import { environment } from '../environments/environment';
import { AppSettings } from './shared/app-settings'
import { AppSettingsService } from './services/app-settings.service';
import { APPSETTINGS } from './shared/defaultSettings';
import { LogService } from 'src/app/services/app-log.service';
import {LOCAL_STORAGE, WebStorageService} from 'angular-webstorage-service';
import { Aircraft } from "./shared/aircraft";
import { Content } from '@angular/compiler/src/render3/r3_ast';
import {TitleService } from './services/title.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit{
className: string;
aircraft: Aircraft[];
isAuthenticated$: Observable<boolean>;
settings$: Observable<AppSettings>;
newSettings: AppSettings;
defaultSettings: AppSettings;
constructor(
private titleService: TitleService,
public appSettingsService: AppSettingsService,
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer,
private adalService: AdalService,
private logger: LogService,
@Inject(LOCAL_STORAGE) private storage: WebStorageService
){
this.settings$ = this.appSettingsService.getSettings();
console.log('settings$: ', this.settings$);
adalService.init(environment.config);
this.matIconRegistry.addSvgIcon(
"historical-tracking-black-48",
this.domSanitizer.bypassSecurityTrustResourceUrl("../assets/historical- tracking-black-48.svg")
);
this.matIconRegistry.addSvgIcon(
"historical-tracking-black-48-inactive",
this.domSanitizer.bypassSecurityTrustResourceUrl("../assets/historical-tracking-black-48-inactive.svg")
);
//this.appSettingsService.getSettings().subscribe(newSettings => this.settings = newSettings);
}
ngOnInit(){
this.className = this.constructor.toString().match(/w /g)[1];
this.adalService.handleWindowCallback();
this.logger.debug(this.className, "adal userInfo: ", this.adalService.userInfo);
this.settings$ = new Observable(observer => {
console.log('in observer');
})
let subscription = this.settings$.subscribe(
newSettings => console.log('NewSettigns: ',newSettings),
)
this.logger.log(this.className, "Settings at startup: ", this.settings);
this.appSettingsService.getSettings().subscribe((newSettings) => {
console.log('settings have changed, new settings are:', newSettings);
})
}
login() {
this.adalService.login();
}
logout() {
this.adalService.logOut();
}
get authenticated(): boolean {
return this.adalService.userInfo.authenticated;
}
}
У меня работает аутентификация (что, как я думал, будет сложно — не получилось), но я ни за что на свете не могу добиться наблюдаемой работы, которая, как я думал, должна была быть простой. Проблема в том, что, похоже, существует так много разных способов сделать это, что все, что я продолжаю читать, в конечном итоге приводит меня в замешательство. Может ли кто-нибудь указать мне правильное направление?
Спасибо….
Комментарии:
1.Из документации, я думаю,
this.storage.get('TRACS3_SETTINGS')
не возвращаетсяobservable
. Скорее егоany
npmjs.com/package/angular-webstorage-service#api . Проверьте.get()
на npmjs.com/package/angular-webstorage-service#usage .Попробуйте то, что предложил @BizzyBob, и дайте мне знать
Ответ №1:
Вы очень близки! Ваша проблема в том, что вы фактически не подписываетесь на observable из AppSettingsService.
Раскомментируйте эту строку:
\this.appSettingsService.getSettings().subscribe(newSettings => this.settings = newSettings);
но измените его на:
this.appSettingsService.settings$.subscribe(newSettings => this.settings = newSettings);
Комментарии:
1. Когда я вношу это изменение, я получаю следующую ошибку из this.settings $=newSettings: Тип ‘AppSettings’ не может быть присвоен типу ‘Observable<AppSettings>’.ts(2322) В типе ‘AppSettings’ отсутствуют следующие свойства из типа ‘Observable<AppSettings>’: _isScalar, source, operator, lift и еще 6.ts(2740)
2. В вашем компоненте приложения измените свое объявление
settings$
наsettings$: AppSettings;
, поскольку оно не является observable . Вы также можете захотеть переименовать без$
.