英文:
Angular subscribe cannot read properties of undefined at interval
问题
我正在尝试每五秒自动更新mat-table
。在首次加载站点时,PrijslijstComponent
的getDrinks()
方法可以正常工作并加载表格。
但是,在初始加载后的每五秒内从interval
调用它时,它会出现以下错误:
关于这种类型错误有一些帖子,但似乎没有解决我的问题的内容。我尝试在getDrinksByGroup()
方法内部做控制台日志记录,但似乎在抛出错误之前根本没有执行任何内容。
我想知道我做错了什么,因为第一个getDrinks()
调用可以正常工作,但之后的调用都不行。另外,我不确定这是否是定期更新mat-table
的正确方法?
prijslijst.component.ts
import { Component } from '@angular/core';
import { MatTableDataSource, MatTableDataSourcePaginator } from '@angular/material/table';
import { DrinksService } from '../drinks/drinks.service';
import { Drink } from '../drinks/drink';
@Component({
selector: 'app-prijslijst',
templateUrl: './prijslijst.component.html',
styleUrls: ['./prijslijst.component.css']
})
export class PrijslijstComponent {
private alcohols: Drink[] = [];
private nonAlcohols: Drink[] = [];
private interval: any;
dataSourceAlcohol!: MatTableDataSource<Drink, MatTableDataSourcePaginator>;
dataSourceNonAlcohol!: MatTableDataSource<Drink, MatTableDataSourcePaginator>;
displayedColumns: string[] = ['name', 'startPrice', 'currentPrice', 'numberSold'];
constructor(private drinksService: DrinksService) {
}
ngOnInit(): void {
this.getDrinks();
this.interval = setInterval(() => this.getDrinks(), 5000);
}
ngOnDestroy() {
if (this.interval) {
clearInterval(this.interval);
}
}
getDrinks() {
console.log('获取饮品');
this.drinksService
.getDrinksByGroup()
.subscribe(({ alcohols, notAlcohols }) => {
if (alcohols !== undefined) {
this.alcohols = alcohols;
}
if (notAlcohols !== undefined) {
this.nonAlcohols = notAlcohols;
}
this.dataSourceAlcohol = new MatTableDataSource(this.alcohols);
this.dataSourceNonAlcohol = new MatTableDataSource(this.nonAlcohols);
});
}
addOrder(drink: Drink) {
drink.numberInOrder = drink.numberInOrder + 1;
}
}
drinks.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { map, Observable, of } from 'rxjs';
import { Drink } from './drink';
@Injectable({
providedIn: 'root'
})
export class DrinksService {
constructor(private http: HttpClient) { }
private url: string = 'http://localhost:3000/drinks';
drinks: Drink[] = [];
setupDrinks(): void {
this.getDrinks().subscribe(drinks => {
drinks.forEach(drink => this.drinks.push(drink))
});
}
getDrinks(): Observable<Drink[]> {
console.log('从后端获取饮品');
return this.http.get<Drink[]>(this.url);
}
getDrinksByGroup(): Observable<{ alcohols: Drink[], notAlcohols: Drink[] }> {
return this.getDrinks().pipe(map(drinks => {
const alcoholDrinks: Drink[] = [];
const notAlcoholDrinks: Drink[] = [];
drinks.forEach(drink => {
if (drink.alcohol) {
alcoholDrinks.push(drink);
} else {
notAlcoholDrinks.push(drink)
}
});
return { alcohols: alcoholDrinks, notAlcohols: notAlcoholDrinks };
}));
}
setSoldOut(drinkName: string) {
this.drinks.filter((drink) => {
if (drink.name === drinkName) {
drink.soldOut = true;
}
})
}
setDrinks(drinks: Drink[]) {
this.drinks = drinks;
}
}
英文:
I'm trying to automatically update a mat-table
every five seconds. Upon first loading the site, the getDrinks()
method of the PrijslijstComponent
works without issue and loads the table.
When calling it from the interval
every five seconds after initial loading though, it gives the following error:
There were a few posts on this type of errors, but nothing which seemed to solve my problem. I tried to do a console log inside the getDrinksByGroup()
method, but nothing at all gets executed there, it seems, before throwing the error.
I'm wondering where I'm going wrong as the first getDrinks()
call works, but none after that. Also, I'm not sure if this is how I should update the mat-table
regularly?
prijslijst.component.ts
import { Component } from '@angular/core';
import {MatTableDataSource, MatTableDataSourcePaginator} from '@angular/material/table';
import { DrinksService } from '../drinks/drinks.service';
import { Drink } from '../drinks/drink';
@Component({
selector: 'app-prijslijst',
templateUrl: './prijslijst.component.html',
styleUrls: ['./prijslijst.component.css']
})
export class PrijslijstComponent {
private alcohols: Drink[] = [];
private nonAlcohols: Drink[] = [];
private interval: any;
dataSourceAlcohol!: MatTableDataSource<Drink, MatTableDataSourcePaginator>;
dataSourceNonAlcohol!: MatTableDataSource<Drink, MatTableDataSourcePaginator>;
displayedColumns: string[] = ['name', 'startPrice', 'currentPrice', 'numberSold'];
constructor(private drinksService: DrinksService) {
}
ngOnInit(): void {
this.getDrinks();
this.interval = setInterval(this.getDrinks, 5000);
}
ngOnDestroy() {
if (this.interval) {
clearInterval(this.interval);
}
}
getDrinks() {
console.log('Getting drinks')
this.drinksService
.getDrinksByGroup()
.subscribe(({alcohols, notAlcohols}) => {
if(alcohols !== undefined) {
this.alcohols = alcohols;
}
if(notAlcohols !== undefined) {
this.nonAlcohols = notAlcohols;
}
this.dataSourceAlcohol = new MatTableDataSource(this.alcohols);
this.dataSourceNonAlcohol = new MatTableDataSource(this.nonAlcohols);
});
}
addOrder(drink: Drink) {
drink.numberInOrder = drink.numberInOrder + 1;
}
}
drinks.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { map, Observable, of } from 'rxjs';
import { Drink } from './drink';
@Injectable({
providedIn: 'root'
})
export class DrinksService {
constructor(private http:HttpClient) { }
private url: string = 'http://localhost:3000/drinks';
drinks: Drink[] = [];
setupDrinks(): void {
this.getDrinks().subscribe(drinks => {
drinks.forEach(drink => this.drinks.push(drink))
});
}
getDrinks(): Observable<Drink[]> {
console.log('Getting drinks from backend')
return this.http.get<Drink[]>(this.url);
}
getDrinksByGroup(): Observable<{alcohols: Drink[], notAlcohols: Drink[] }> {
return this.getDrinks().pipe(map(drinks => {
const alcoholDrinks: Drink[] = [];
const notAlcoholDrinks: Drink[] = [];
drinks.forEach(drink => {
if(drink.alcohol) {
alcoholDrinks.push(drink);
} else {
notAlcoholDrinks.push(drink)
}
});
return { alcohols: alcoholDrinks, notAlcohols: notAlcoholDrinks };
}));
}
setSoldOut(drinkName: string) {
this.drinks.filter((drink) => {
if(drink.name === drinkName) {
drink.soldOut = true;
}
})
}
setDrinks(drinks: Drink[]) {
this.drinks = drinks;
}
}
答案1
得分: 2
你的问题出在这一行:
this.interval = setInterval(this.getDrinks, 5000);
只需替换为:
this.interval = setInterval(() => this.getDrinks(), 5000);
这样就能正常工作。
英文:
Your issue is on this line:
this.interval = setInterval(this.getDrinks, 5000);
Just replace it by:
this.interval = setInterval(() => this.getDrinks(), 5000);
and it will work
答案2
得分: 1
问题出在this
对象上。这就是你得到未定义值的原因。
基本上,当setInterval
回调触发时,它将进入getDrinks
函数,但该函数中的this
对象并不是你所认为的那样。
你可以选择像这样做:
// 明确绑定`this`对象到你想要的作用域(组件)
this.interval = setInterval(this.getDrinks.bind(this), 5000);
或者:
// 箭头函数会自动将`this`对象绑定到其词法(包装范围)
this.interval = setInterval(() => this.getDrinks(), 5000);
或者以“正确”的方式来做,那就是使用响应式方法(rxjs 适用于此),例如:
destroy$ = new Subject();
ngOnInit() {
// 每5秒触发一次计时器(第一个参数为0时,将立即触发第一次,无延迟)
timer(0, 5000)
.pipe(
// 调用你的服务
switchMap(_ => this.drinksService.getDrinksByGroup()),
// 在ngDestroy时取消订阅
takeUntil(this.destroy$))
.subscribe(result => {
// 处理结果
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
英文:
The problem here is in this
object. Thats why you got undefined value.
Basically when setInterval callback fires, it will go in getDrinks
function but this
object in that function is not what you think it is.
You can either do something like:
// Explicitly bind `this` object to the scope that you want (component)
this.interval = setInterval(this.getDrinks.bind(this), 5000);
or :
// Arrow function automatically bind `this` object to its lexical (wrapping scope)
this.interval = setInterval(() = this.getDrinks(), 5000);
Or do it in a "right" way, and that is to use reactive approach (rxjs is for that) such as:
destroy$ = new Subject();
ngOnInit() {
// Trigger timer each 5seconds (with 0 as first argument it will trigger first time without delay)
timer(0, 5000)
.pipe(
// Call your service
switchMap(_ => this.drinksService.getDrinksByGroup()),
// Unsubscribe on ngDestroy
takeUntil(this.destroy$))
.subscribe((result) => {
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论