Angular 4 - 通过非角度动作异步更改变量时的渲染模板

时间:2017-04-25 15:38:38

标签: angular typescript asynchronous rxjs cartodb

我的应用程序是使用最新的@ angular / cli生成的。

Angular v。:4.0.3, RxJS v。: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在数据未成功解析时不应迭代它。

我尝试过的解决方案:

  1. RxJS和Observable.subscribe()不起作用。 Angular认为异步操作只是浏览器事件,http请求和计时器。我认为Carto的请求是一个Jquery回调,而不是异步。
    1. * ngIf 也不起作用。它仅在变量等于null时隐藏。就我而言,数据&#39;等于:
    2. {"_callbacks":{"done":{"tail":{},"next":{"next":{}}},"error":{"tail":{},"next":{"next":{}}}}}

      收到回复后,模板将不会再次呈现。

      1. Elvis运算符(?将变量标记为可选)只能与params一起使用。如果我像这样使用它会导致模板渲染错误:
      2. <tr *ngFor="let entry of data?">

        1. 异步管道:
        2. <tr *ngFor="let entry of (data | async)">

          会导致此错误:

          ERROR Error: Uncaught (in promise): Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'

          1. ngOnChanges 没有看到我的变量发生了变化。它的输出中没有出现:

            ngOnChanges(changes) { console.log('All changes: ', changes); }

          2. 之前有没有人遇到这样的问题? 我将不胜感激任何帮助。提前谢谢。

1 个答案:

答案 0 :(得分:1)

选项1 (使用rxjs)

你可以让你的服务返回一个Observable并使用Subject在sql执行完成后将数据推送到这个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 (带回调)

您可以更改服务以调用某些回调函数,而不是返回数据:

@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应该按预期工作了。 data属性将为空,直到服务用回调中的某些行填充它。

<tbody>
  <tr *ngFor="let entry of data">
    <th scope="row">{{entry.year}}</th>
  </tr>
</tbody>
相关问题