Angular 4 - 通过非 angular 操作异步更改变量时呈现模板
Angular 4 - Render template when variable was changed asynchronously via non-angular action
我的申请是用最新的@angular/cli生成的。
Angular v.: 4.0.3,
RxJS 诉:5.1,
Zone.js: 0.8.4,
打字稿:2.2.2
我需要将 CartoDB 集成到我的应用程序中。我创建了一个请求和存储数据的服务。这是:
// ========== carto.service.ts ==========
import {Injectable} from '@angular/core';
declare var cartodb: any;
@Injectable()
export class CartoService {
private _sql: any;
private _data: any;
constructor() {
this._data = this.getDataFromCarto('SELECT * FROM table');
}
get data() {
return this._data;
}
getDataFromCarto(query: string): any {
this._sql = new cartodb.SQL({user: username});
return this._sql.execute(query).done((data) => {
return data.rows;
}).error((errors) => {
console.error(`Was not able to get data: ${errors}`);
});
}
}
我没有找到 cartodb.js 的类型定义,也无法自己编写。所以我只是将它连接为 JS 文件并声明了一个与 carto 的全局变量匹配的新变量。
我的组件:
// ========== widget.component.ts ===========
import {Component, OnInit} from '@angular/core';
import {CartoService} from 'carto.service';
@Component({
selector: 'app-widget',
templateUrl: 'widget.component.html',
styleUrls: ['widget.component.scss']
})
export class WidgetTableComponent implements OnInit {
public data: any;
constructor(private carto: CartoService) {
super();
this.data = this.carto.data;
}
ngOnInit() {
}
}
作为最后一步,我想在模板中显示此数据:
// ========= widget.component.html ==========
<tbody>
<tr *ngFor="let entry of data">
<th scope="row">{{entry.year}}</th>
</tr>
</tbody>
虽然数据未解析,但我收到了一个带回调的对象。 *ngFor 不应在数据未成功解析时迭代它。
我试过的解决方案:
- RxJS 和 Observable.subscribe() 不起作用。 Angular认为异步动作只有浏览器事件、http请求和定时器。我猜 Carto 的请求是一个 Jquery 回调,不被认为是异步的。
- *ngIf 也不起作用。它仅在变量等于 null 时隐藏。在我的例子中 'data' 等于:
{"_callbacks":{"done":{"tail":{},"next":{"next":{}}},"error":{"tail":{},"next":{"next":{}}}}}
收到响应后,将不会再次呈现模板。
- Elvis 运算符(?将变量标记为可选)只能与参数一起使用。如果我这样使用它会导致模板渲染错误:
<tr *ngFor="let entry of data?">
- 异步管道:
<tr *ngFor="let entry of (data | async)">
会导致这个错误:
ERROR Error: Uncaught (in promise): Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'
ngOnChanges 没有看到我的变量发生变化。它不存在于以下输出中:
ngOnChanges(changes) {
console.log('All changes: ', changes);
}
有没有人遇到过这样的问题?
我将不胜感激任何帮助。提前致谢。
选项 1(使用 rxjs)
您可以使您的服务 return 成为一个 Observable,并在 sql 执行完成后使用 Subject 将数据推送到此 Observable:
@Injectable()
export class CartoService {
private dataSbj = new Subject<any>();
private dataObs = this.dataSbj.asObservable();
getDataFromCarto(): Observable<any> {
let sql = new cartodb.SQL({user: username});
sql.execute('SELECT * FROM table')
.done(data => this.dataSbj.next(data.rows))
.error((errors) => {
console.error(`Was not able to get data: ${errors}`);
});
return this.dataObs;
}
}
组件:
@Component({
selector: 'app-widget',
templateUrl: 'widget.component.html',
styleUrls: ['widget.component.scss']
})
export class WidgetTableComponent {
data: Observable<any>;
constructor(private carto: CartoService) {
super();
this.data = carto.getDataFromCarto();
}
}
模板:
<tbody>
<tr *ngFor="let entry of data | async">
<th scope="row">{{entry.year}}</th>
</tr>
</tbody>
选项 2(带回调)
您可以更改您的服务以调用一些回调函数而不是 returning 数据:
@Injectable()
export class CartoService {
getDataFromCarto(callback: (rows: any) => void): void {
let sql = new cartodb.SQL({user: username});
sql.execute('SELECT * FROM table')
.done(data => callback(data.rows))
.error((errors) => {
console.error(`Was not able to get data: ${errors}`);
});
}
}
在组件中,您可以调用您的服务并传递一个回调函数,该回调函数会在数据就绪时设置数据:
@Component({
selector: 'app-widget',
templateUrl: 'widget.component.html',
styleUrls: ['widget.component.scss']
})
export class WidgetTableComponent {
data: any;
constructor(private carto: CartoService) {
super();
carto.getDataFromCarto(rows => {this.data = rows});
}
}
现在你的 *ngFor 应该可以正常工作了。数据 属性 将是空的,直到服务在回调中用一些行填充它。
<tbody>
<tr *ngFor="let entry of data">
<th scope="row">{{entry.year}}</th>
</tr>
</tbody>
我的申请是用最新的@angular/cli生成的。
Angular v.: 4.0.3, RxJS 诉:5.1, Zone.js: 0.8.4, 打字稿:2.2.2
我需要将 CartoDB 集成到我的应用程序中。我创建了一个请求和存储数据的服务。这是:
// ========== carto.service.ts ==========
import {Injectable} from '@angular/core';
declare var cartodb: any;
@Injectable()
export class CartoService {
private _sql: any;
private _data: any;
constructor() {
this._data = this.getDataFromCarto('SELECT * FROM table');
}
get data() {
return this._data;
}
getDataFromCarto(query: string): any {
this._sql = new cartodb.SQL({user: username});
return this._sql.execute(query).done((data) => {
return data.rows;
}).error((errors) => {
console.error(`Was not able to get data: ${errors}`);
});
}
}
我没有找到 cartodb.js 的类型定义,也无法自己编写。所以我只是将它连接为 JS 文件并声明了一个与 carto 的全局变量匹配的新变量。
我的组件:
// ========== widget.component.ts ===========
import {Component, OnInit} from '@angular/core';
import {CartoService} from 'carto.service';
@Component({
selector: 'app-widget',
templateUrl: 'widget.component.html',
styleUrls: ['widget.component.scss']
})
export class WidgetTableComponent implements OnInit {
public data: any;
constructor(private carto: CartoService) {
super();
this.data = this.carto.data;
}
ngOnInit() {
}
}
作为最后一步,我想在模板中显示此数据:
// ========= widget.component.html ==========
<tbody>
<tr *ngFor="let entry of data">
<th scope="row">{{entry.year}}</th>
</tr>
</tbody>
虽然数据未解析,但我收到了一个带回调的对象。 *ngFor 不应在数据未成功解析时迭代它。
我试过的解决方案:
- RxJS 和 Observable.subscribe() 不起作用。 Angular认为异步动作只有浏览器事件、http请求和定时器。我猜 Carto 的请求是一个 Jquery 回调,不被认为是异步的。
- *ngIf 也不起作用。它仅在变量等于 null 时隐藏。在我的例子中 'data' 等于:
{"_callbacks":{"done":{"tail":{},"next":{"next":{}}},"error":{"tail":{},"next":{"next":{}}}}}
收到响应后,将不会再次呈现模板。
- Elvis 运算符(?将变量标记为可选)只能与参数一起使用。如果我这样使用它会导致模板渲染错误:
<tr *ngFor="let entry of data?">
- 异步管道:
<tr *ngFor="let entry of (data | async)">
会导致这个错误:
ERROR Error: Uncaught (in promise): Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'
ngOnChanges 没有看到我的变量发生变化。它不存在于以下输出中:
ngOnChanges(changes) { console.log('All changes: ', changes); }
有没有人遇到过这样的问题? 我将不胜感激任何帮助。提前致谢。
选项 1(使用 rxjs)
您可以使您的服务 return 成为一个 Observable,并在 sql 执行完成后使用 Subject 将数据推送到此 Observable:
@Injectable()
export class CartoService {
private dataSbj = new Subject<any>();
private dataObs = this.dataSbj.asObservable();
getDataFromCarto(): Observable<any> {
let sql = new cartodb.SQL({user: username});
sql.execute('SELECT * FROM table')
.done(data => this.dataSbj.next(data.rows))
.error((errors) => {
console.error(`Was not able to get data: ${errors}`);
});
return this.dataObs;
}
}
组件:
@Component({
selector: 'app-widget',
templateUrl: 'widget.component.html',
styleUrls: ['widget.component.scss']
})
export class WidgetTableComponent {
data: Observable<any>;
constructor(private carto: CartoService) {
super();
this.data = carto.getDataFromCarto();
}
}
模板:
<tbody>
<tr *ngFor="let entry of data | async">
<th scope="row">{{entry.year}}</th>
</tr>
</tbody>
选项 2(带回调)
您可以更改您的服务以调用一些回调函数而不是 returning 数据:
@Injectable()
export class CartoService {
getDataFromCarto(callback: (rows: any) => void): void {
let sql = new cartodb.SQL({user: username});
sql.execute('SELECT * FROM table')
.done(data => callback(data.rows))
.error((errors) => {
console.error(`Was not able to get data: ${errors}`);
});
}
}
在组件中,您可以调用您的服务并传递一个回调函数,该回调函数会在数据就绪时设置数据:
@Component({
selector: 'app-widget',
templateUrl: 'widget.component.html',
styleUrls: ['widget.component.scss']
})
export class WidgetTableComponent {
data: any;
constructor(private carto: CartoService) {
super();
carto.getDataFromCarto(rows => {this.data = rows});
}
}
现在你的 *ngFor 应该可以正常工作了。数据 属性 将是空的,直到服务在回调中用一些行填充它。
<tbody>
<tr *ngFor="let entry of data">
<th scope="row">{{entry.year}}</th>
</tr>
</tbody>