【问题标题】:Building a data service in Angular 5在 Angular 5 中构建数据服务
【发布时间】:2018-07-12 05:39:43
【问题描述】:

我正在尝试在 Angular 5 中构建一个数据服务,该服务从平面文件中读取数据并将其传递给组件进行显示。该文件位于相对于数据服务的 ../data-files 目录中。

问题是我很难验证服务是否正在向组件提供数据。为了简单起见,我尝试放置一个 console.log(data);在组件的构造函数中,但到目前为止我一直无法确定发生了什么。

这是我的数据服务代码:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';

@Injectable()
export class DataService {
  dataUrl = '../data-files/migrations.json';
  DATA;

  constructor(private http: HttpClient) {};

  getData(): Observable<any>{
    return this.http.get(this.dataUrl);
  };

  ngOnInit() {
    this.getData();
  };
}

这就是我的组件的样子:

import { Component, OnInit, OnChanges, ViewChild, ElementRef, Input, ViewEncapsulation } from '@angular/core';
import * as d3 from 'd3';
import { DataService } from '../services/data.service';


@Component({
  selector: 'app-mosaic-view',
  templateUrl: './mosaic-view.component.html',
  styleUrls: ['./mosaic-view.component.css'],
  encapsulation: ViewEncapsulation.None,
  // providers: [DataService]
})
export class MosaicViewComponent implements OnInit, OnChanges {
  @ViewChild('chart') private chartContainer: ElementRef;
  // @Input() private data: Array<any>;
  data;
  // chart variables go here.
  private chart: any;
  private width: number;
  private height: number;

  constructor(private dataService: DataService) {
    console.log("mosaic-view constructed...");  // does not print anything.
  };

  ngOnInit() {
    this.getData();
    this.createChart();
    if(this.data){
      this.updateChart();
    }
  };

  ngOnChanges() {
    if(this.chart){
      this.updateChart();
    }
  };

  getData(): void {
    this.data = this.dataService.getData().subscribe(data => this.data = data);
    console.log(this.data);  // does not print anything.
  }

  createChart(){
    // creates the mosaic viz, build on d3.treemap()
    // TODO
  };

  updateChart(){
    // updates the viz when the data changes.
    // TODO
  };

}

console.log(...) 语句从不打印,即使我将它们直接放在构造函数中。

在我的 app.component.html 文件中,我有一个对选择器的引用,如下所示:

...
<div id="app-mosaic-view"></div>
...

在 linter 或控制台中没有显示错误。我已尝试遵循设置此类服务的最佳实践,但有些地方不对劲,我无法确切说明是什么。

我们将不胜感激。

编辑:我做了一些建议的更改,但现在我遇到了我尝试加载的数据文件的 404 错误。我检查了路径,甚至尝试明确指定路径无济于事。知道为什么找不到文件吗?

新组件:

import { Component, OnInit, OnChanges, ViewChild, ElementRef, Input, ViewEncapsulation } from '@angular/core';
import * as d3 from 'd3';
import { DataService } from '../services/data.service';

@Component({
  selector: 'app-mosaic-view',
  templateUrl: './mosaic-view.component.html',
  styleUrls: ['./mosaic-view.component.css'],
  encapsulation: ViewEncapsulation.None,
  // providers: [DataService]
})
export class MosaicViewComponent implements OnInit, OnChanges {
  @ViewChild('chart') private chartContainer: ElementRef;
  componentName: string = 'Mosaic View';
  private data: any[];
  private errorMessage: string;

  // chart variables go here.
  private chart: any;
  private width: number;
  private height: number;

  constructor(private dataService: DataService) {
    console.log("mosaic-view constructed...");
  };

  ngOnInit() {
    this.getData();
    if(this.data){
      this.updateChart();
    }
  };

  ngOnChanges() {
    if(this.chart){
      this.updateChart();
    }
  };

  getData(): void {
    this.dataService.getData().subscribe(
      (data: any[]) => {
        // this.data = data;
        this.data = data;
        this.createChart(data);
      },
      (error: any) => this.errorMessage = <any>error
    );
    console.log(this.data);
  }

  createChart(data){
    // creates the mosaic viz, build on d3.treemap()
    // TODO
  };

  updateChart(){
    // updates the viz when the data changes.
    // TODO
  };

}

新服务:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import {catchError, map, tap } from 'rxjs/operators';

@Injectable()
export class DataService {
  dataUrl: string = '../consolidated_cohorts/src/app/data-files/migrations.json';

  constructor(private http: HttpClient) {
    console.log("data service called...")
    this.getData();
  };


  getData(): Observable<any>{
    return this.http.get<any[]>(this.dataUrl)
                    .pipe(
                      tap(data => console.log(data)),
                      // catchError(this.handleError)  // TODO
                    );
  };

  // private handleError: {
  //   console.log("there was an error.");
  // }
}

404 错误: http://localhost:4200/data-files/migrations.json404(未找到)

【问题讨论】:

    标签: angular service


    【解决方案1】:

    您错过了订阅。 Observable 在订阅之前不会执行。而任何想要在获取数据后执行的代码都需要进入订阅方法的回调中。

    我的代码如下所示:

    组件

    ngOnInit(): void {
        this.productService.getProducts().subscribe(
            (products: IProduct[]) => {
                this.products = products;
                this.filterComponent.listFilter =
                    this.productParameterService.filterBy;
            },
            (error: any) => this.errorMessage = <any>error
        );
    }
    

    我将一个函数传递给 subscribe 方法,该方法将返回的产品存储在一个局部变量(我用于绑定)中,并根据任何先前保留的过滤器设置过滤列表。 (这只是我代码中的一个示例……您的回调中会有不同的代码。)

    您的代码可能如下所示:

      ngOnInit() {
        this.createChart();
        this.getData().subscribe(data => this.updateChart());
      };
    

    是的!正如 Narm 提到的,服务中没有 OnInit 生命周期挂钩。这是我的服务的样子:

    服务

    import { catchError, tap } from 'rxjs/operators';
    
    
    getProducts(): Observable<IProduct[]> {
        return this.http.get<IProduct[]>(this.productsUrl)
                        .pipe(
                            tap(data => console.log(JSON.stringify(data))),
                            catchError(this.handleError)
                        );
    }
    

    您可以将其缩短为:

    getProducts(): Observable<IProduct[]> {
        return this.http.get<IProduct[]>(this.productsUrl);
    }
    

    我添加了tap 运算符以将结果记录到控制台以进行调试。添加一些异常处理是“最佳实践”。

    【讨论】:

    • 我不熟悉 tap() 的使用,我的 IDE 说它是一个未知函数。你能详细说明一下如何合并 tap() 吗?
    • 回复:我上面的编辑 - 请求返回 404 的原因是我未能在 .angular-cli.json 中将文件位置声明为“资产”。当我声明它时,错误自行解决。
    【解决方案2】:

    生命周期钩子,如 ngOnInit() 与指令和组件一起工作。它们不适用于其他类型,例如服务。在您的服务中,您应该删除 ngOnInit 并将其逻辑移动到您的构造函数中,如下所示:

    constructor(private http: HttpClient) {
       this.getData();
     };
    

    【讨论】:

    • 这是对 DeborahK 提到的需要订阅组件中的 observable 的补充。抱歉,我会将其作为评论添加到她的帖子中,但我没有足够的声望点。
    • 好收获!是的……这也是!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-10
    • 1970-01-01
    • 1970-01-01
    • 2018-06-17
    • 2018-10-01
    相关资源
    最近更新 更多