![](/img/trans.png)
[英]How to return an observable from service in angular after chaining pipe operations
[英]Make angular service return class field as a observable
这是我想要实现的目标:有一个组件中有一个 UserService 。 登录后,我想调用该服务以从后端检索用户数据并将其存储在服务字段中。 然后在我重新加载主页后,我希望该服务不再调用后端,只返回以前存储的数据。 此外,我希望在加载数据之前不加载一些 div。 我怎样才能做到这一点? 我应该让getAllUSerInfoNormal()方法返回 observable,如果可能的话,我应该怎么做? 还是有其他方法?
这是我现在得到的:
服务 class:
@Injectable()
export class AccountService {
userAccount : UserAccount;
constructor(private _httpClient: HttpClient) {
}
getAllUSerInfoNormal(): UserAccount {
if (!this.userAccount) {
this._httpClient.get<UserAccount>(Constants.apiRoot + 'account/accountInfo').subscribe(user => {
this.userAccount = user;
return this.userAccount
})
} else {
return this.userAccount;
}
}
组件 class:
@Component({
selector: 'app-main-view',
templateUrl: './main-view.component.html',
styleUrls: ['./main-view.component.scss']
})
export class MainViewComponent implements OnInit {
private account: UserAccount;
private isDataLoaded = false;
constructor(private _router: Router,
private _accountService: AccountService) {
setInterval(() => {
this.updateResources();
}, 1);
}
ngOnInit() {
this._accountService.getAllUSerInfoNormal();
}
updateResources(){
}
}
组件模板:
<div class="ingame-menu">
<div *ngIf="isDataLoaded">
<div class="resources">
<div class="resource"><img src="../../../assets/images/resources/gold.png" title="Gold"/>
<br/> {{account.gold}}</div>
<div class="resource"><img src="../../../assets/images/resources/wood.png" title="Wood"/>
<br/> {{account.wood}}</div>
<div class="resource"><img src="../../../assets/images/resources/stone.png" title="Stone"/>
<br/> {{account.stone}}</div>
<div class="resource"><img src="../../../assets/images/resources/meat.png" title="Meat"/>
<br/> {{account.people}} </div>
</div>
</div>
<div class="ingame-nav">
<ul>
<li><a routerLink="overview">Overview</a></li>
<li><a routerLink="population">Population</a></li>
<li><a routerLink="resources">Resources</a></li>
<li><a routerLink="research">Research</a></li>
<li><a routerLink="buildings">Buildings</a></li>
<li><a routerLink="barracks">Barracks</a></li>
<li><a routerLink="tavern">Tavern</a></li>
<li><a routerLink="defence">Defence</a></li>
</ul>
</div>
<div id="content">
<router-outlet></router-outlet>
</div>
</div>
您可以在第一次加载后将加载的用户保存在 lcoalStorage 中,然后在以下访问中读取它。
这是一个简单的学习解决方案。 但在实际应用场景中,您不应该有这样的“无限时间”缓存。 并且有时必须有办法更新缓存。
将服务 class更改为:
@Injectable()
export class AccountService {
get userAccount() {
return JSON.parse(localStorage.getItem('user'));
};
constructor(private _httpClient: HttpClient) {
}
getAllUSerInfoNormal(forceBanckendCall: boolean): Observable<UserAccount> {
if (this.userAccount && !forceBackendCall) {
return of(this.userAccount);
}
return this._httpClient.get<UserAccount>(Constants.apiRoot + 'account/accountInfo')
.pipe(
tap(user => localStorage.setItem('user', JSON.stringify(user)))
);
}
然后组件 class到:
@Component({
selector: 'app-main-view',
templateUrl: './main-view.component.html',
styleUrls: ['./main-view.component.scss']
})
export class MainViewComponent implements OnInit {
private account: UserAccount;
private isDataLoaded = false;
constructor(private _router: Router,
private _accountService: AccountService) {
setInterval(() => {
this.updateResources();
}, 1);
}
ngOnInit() {
this._accountService.getAllUSerInfoNormal().subscribe(account => this.userAccount = account);
}
updateResources(){
}
}
然后在您的组件模板中放置一个*ngIf="userAccount"
在您想要在用户出现后立即加载的元素中。
您无法像尝试那样同步地从订阅中返回值。 来自 observable 的变量是异步发出的。 有关异步数据的更多信息,请参阅此处。 简而言之,该值仅在订阅内可用。
有多种方法可以缓存来自 observable 的通知。 最快的方法之一是在服务中使用多播 observable(如Subject
)来保存数据。 我将说明使用带有缓冲区 1 的ReplaySubject
,因为它可以保持当前值。
服务
import { ReplaySubject } from 'rxjs';
@Injectable()
export class AccountService {
userAccountSource: ReplaySubject<UserAccount> = new ReplaySubject<UserAccount>(1);
userAccount$ = this.userAccountSource.asObservable();
constructor(private _httpClient: HttpClient) {
this.getAllUSerInfoNormal().subscribe(); // <-- push to cache on app start
}
getAllUSerInfoNormal(): Observable<UserAccount> {
return this._httpClient.get<UserAccount>(Constants.apiRoot + 'account/accountInfo').pipe(
tap({
next: user => this.userAccountSource.next(user),
error: error => { } // <-- handle error
})
);
}
}
async
pipe。 除了保持代码整洁之外,它还将处理开放订阅的潜在泄漏问题。Controller
@Component({...})
export class MainViewComponent implements OnInit {
private isDataLoaded = false;
constructor(
private _router: Router,
private _accountService: AccountService
) {
setInterval(() => {
this.updateResources();
}, 1);
}
ngOnInit() {
}
updateResources() {
}
}
模板
<div class="ingame-menu">
<div *ngIf="(_accountService.userAccount$ | async) as account">
<div class="resources">
<div class="resource"><img src="../../../assets/images/resources/gold.png" title="Gold" />
<br /> {{account.gold}}</div>
<div class="resource"><img src="../../../assets/images/resources/wood.png" title="Wood" />
<br /> {{account.wood}}</div>
<div class="resource"><img src="../../../assets/images/resources/stone.png" title="Stone" />
<br /> {{account.stone}}</div>
<div class="resource"><img src="../../../assets/images/resources/meat.png" title="Meat" />
<br /> {{account.people}} </div>
</div>
</div>
<div class="ingame-nav">
<ul>
<li><a routerLink="overview">Overview</a></li>
<li><a routerLink="population">Population</a></li>
<li><a routerLink="resources">Resources</a></li>
<li><a routerLink="research">Research</a></li>
<li><a routerLink="buildings">Buildings</a></li>
<li><a routerLink="barracks">Barracks</a></li>
<li><a routerLink="tavern">Tavern</a></li>
<li><a routerLink="defence">Defence</a></li>
</ul>
</div>
<div id="content">
<router-outlet></router-outlet>
</div>
</div>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.