在Angular 5中构建数据服务

时间:2018-02-01 19:39:34

标签: angular service

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

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

这是我的数据服务代码:

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.json 404(未找到)

2 个答案:

答案 0 :(得分:2)

您缺少订阅。 Observable在订阅之前不会执行。在获取数据之后,您想要执行的任何代码都需要进入subscribe方法的回调。

我的代码如下所示:

<强>组件

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运算符,将结果记录到控制台以进行调试。这是&#34;最佳实践&#34;添加一些异常处理。

答案 1 :(得分:2)

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

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