#angular #rxjs
#angular #rxjs
Вопрос:
Я думал, что наконец-то начинаю понимать это, но, очевидно, я все еще чего-то не понимаю. Я пытаюсь создать observable, используя BehaviorSubject в моей службе настроек приложения. Я думаю, что понимаю шаги по правильному созданию observable. Но теперь, когда у меня есть несколько новых значений для обновления observable, Angular выдает среду выполнения, сообщающую мне, что next не определен для моей службы поведения.
Вот обновленный код:
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 {
className: string;
settings: AppSettings;
@Input()
// Use of BehavioirSubject. This is where you post values into
private _settings$: BehaviorSubject<AppSettings> = new BehaviorSubject<AppSettings>(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.className = this.constructor.toString().match(/w /g)[1];
/*
* First, we check to see if this is the first time the app has
* been run on this machine. If it is, then local storage will return
* null when we ask for the settings. We'll populate local storage
* with the default settings values before we continue on setting up
* the service.
*/
this.settings = this.storage.get('TRACS3_SETTINGS');
if ( this.settings == null ) {
try {
this.storage.set('TRACS3_SETTINGS', APPSETTINGS);
}
catch(e) {
console.log(this.className, 'Machine does not support local storage. ');
}
}
/*
AppSettings are now initialized, set the initial value of them for all other
components will use to monitor them.
*/
console.log(this.className, 'about to set observable iniitial values');
this.settings = this.storage.get('TRACS3_SETTINGS');
this._settings$ = this.storage.get('TRACS3_SETTINGS');
console.log(this.className, 'just changed observabe to ', this._settings$);
}
public saveSettings(settings: AppSettings): void{
console.log(this.className, 'saving settings in service, new settings: ', settings);
//this._settings$.post(settings)
this.storage.set('TRACS3_SETTINGS', settings);
this._settings$.next( settings );
}
//public getSettings(): AppSettings {
//eturn this.storage.get('TRACS3_SETTINGS');
//}
}
Теперь я получаю разные ошибки. Если я попытаюсь установить Observer с помощью этой строки кода:
this._settings$ = this.settings;
Я получаю сообщение об ошибке:
Type 'AppSettings' is missing the following properties from type 'BehaviorSubject<AppSettings>': _value, value, _subscribe, getValue, and 19 more.ts(2740)
Но когда я присваиваю ему значение, указанное выше (=this.storage.get …) Теперь я получаю ошибку времени выполнения
Здесь я в тупике. Я не знаю, что еще, кроме, может быть, полного отказа от angular.
Комментарии:
1. «@Input()» — привязка ввода не должна передаваться в службу. Глядя на импорт, я рекомендую обновить ваш проект до последней версии Angular.
2. вы пропустили
BehavoirSubject
тип при его создании.private _settings$: BehaviorSubject<AppSettings> = new BehaviorSubject<AppSettings>(APPSETTINGS)
3. Добавлен тип AppSettings и по-прежнему выдается та же ошибка. Я вообще не понял комментарий Роберта. Слишком загадочно для этого слабоумного.
4. возможно, эта строка перезаписывает ее: this._settings $ = this.storage.get(‘TRACS3_SETTTINGS’); попробуйте закомментировать или изменить ее для вызова метода «next(»
5.@robert прав, вы переназначаете свой
_settings$
BehaviorSubject
вconstructor()
Ответ №1:
В вашем конструкторе вы переназначаете _settings$
с помощью this._settings$ = this.storage.get('TRACS3_SETTINGS');
Теперь поле имеет любое значение для TRACS3_SETTING
, и я предполагаю, что это не является каким-либо наблюдаемым.
Вы должны использовать this._settings$.next(this.storage.get('TRACS3_SETTINGS'));
для изменения его внутреннего значения
Ответ №2:
Я спросил об этом одного из инженеров (@JavRufus) на работе (я не программировал много лет и снова учусь этому искусству). Он придумал для меня следующий пример.
appsettings.service.ts:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { AppSettings } from '../model/appsettings';
@Injectable({
providedIn: 'root'
})
export class AppSettingsService {
private appSettings: AppSettings;
private _appSettingsSubject: BehaviorSubject<AppSettings> = new BehaviorSubject(this.appSettings);
constructor() {
if (localStorage amp;amp; localStorage.getItem('APP-SETTINGS') !== 'undefined') {
this.appSettings = JSON.parse(localStorage.getItem('APP-SETTINGS'));
} else {
this.appSettings = new AppSettings();
localStorage.setItem('APP-SETTINGS', JSON.stringify(this.appSettings));
}
this._appSettingsSubject.next(this.appSettings);
}
get behaviorSubject() {
return this._appSettingsSubject;
}
set updateAppSettings(newAppSettings: AppSettings) {
console.log('Service; update sessings: ' this.appSettings.userName '; ' this.appSettings.password);
this.appSettings = newAppSettings;
if (localStorage) {
localStorage.setItem('APP-SETTINGS', JSON.stringify(this.appSettings));
}
this._appSettingsSubject.next(this.appSettings);
}
}
пример-client.component.ts:
import { Component, OnInit } from '@angular/core';
import { AppSettings } from '../model/appsettings';
import { AppSettingsService } from '../config/appsettings.service';
@Component({
selector: 'app-sample-client',
templateUrl: './sample-client.component.html',
styleUrls: ['./sample-client.component.css']
})
export class SampleClientComponent implements OnInit {
appSettings: AppSettings;
constructor(private appSettingsService: AppSettingsService) {}
ngOnInit() {
this.appSettings = this.appSettingsService.behaviorSubject.getValue();
}
updateAppSettings() {
console.log('Client; update sessings: ' this.appSettings.userName '; ' this.appSettings.password);
this.appSettingsService.updateAppSettings = this.appSettings;
}
}
наконец, за ним следует sample-receiver.component.ts:
import { Component, OnInit } from '@angular/core';
import { AppSettings } from '../model/appsettings';
import { AppSettingsService } from '../config/appsettings.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-sample-receiver',
templateUrl: './sample-receiver.component.html',
styleUrls: ['./sample-receiver.component.css']
})
export class SampleReceiverComponent implements OnInit {
appSettings: AppSettings;
appSettingsSub: Subscription;
constructor(private appSettingsService: AppSettingsService) {}
ngOnInit() {
this.appSettings = this.appSettingsService.behaviorSubject.getValue();
this.appSettingsSub = this.appSettingsService.behaviorSubject.asObservable().subscribe(value => {
this.appSettings = value;
});
}
}
и, конечно, класс appsettings.ts
export class AppSettings {
userName: string;
password: string;
constructor() {}
}
Используйте это в качестве отправной точки, и у вас не должно возникнуть проблем с настройкой сервиса для хранения настроек приложений, а затем с компонентом для их изменения и компонентом для получения уведомления об их изменении.
Надеюсь, это поможет кому-то еще не тратить 3 дня на поиски решения, как это сделал я!