首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在模板尝试呈现数据之前确保数据已经到达?

如何在模板尝试呈现数据之前确保数据已经到达?
EN

Stack Overflow用户
提问于 2016-07-22 16:00:06
回答 2查看 900关注 0票数 1

我做了一个应用程序的角-cli,不变和Redux。我遵循了文章的说明,这篇文章没有描述如何获取数据。我的应用程序需要用异步http调用的数据初始化Redux存储。

我有一个组件来列出具有模板的数据。该组件从存储区获取数据,该存储依赖于进行http调用的服务。http调用可以工作,但是应用程序会抛出一个异常,表明列表组件正在尝试在数据到达之前获取数据。

我的存储库是这里

该应用程序的演示是这里

错误消息:

代码语言:javascript
复制
main.js:21 TypeError: Cannot read property 'getState' of undefined
    at a.get [as objections] (https://dancancro.github.io/bernierebuttals/main.js:18:22268)
    at a._View_a0.detectChangesInternal (a.template.js:189:37)
    at a.detectChanges (https://dancancro.github.io/bernierebuttals/main.js:32:13138)
    at a.detectViewChildrenChanges (https://dancancro.github.io/bernierebuttals/main.js:32:13774)
    at a._View_a_Host0.detectChangesInternal (a.template.js:34:8)
    at a.detectChanges (https://dancancro.github.io/bernierebuttals/main.js:32:13138)
    at a.detectContentChildrenChanges (https://dancancro.github.io/bernierebuttals/main.js:32:13588)
    at a.detectChangesInternal (https://dancancro.github.io/bernierebuttals/main.js:32:13345)
    at a.detectChanges (https://dancancro.github.io/bernierebuttals/main.js:32:13138)
    at a.detectViewChildrenChanges (https://dancancro.github.io/bernierebuttals/main.js:32:13774)

下面是代码的一些相关部分:(我正在研究这个问题。存储库包含当前代码)

list.component.html

代码语言:javascript
复制
...

<ul id="objection-list" [sortablejs]="store.objections" [sortablejsOptions]="options" (update)="setTouched()">
<li *ngFor="let objection of store.objections">
   <list-objection 
   [objection]="objection" 
   [editable]="editable"
   (onEdit)="setTouched()" 
   (onReordered)="setReordered(objection)"
   ></list-objection>
</li>
</ul>

...

list.component.ts

代码语言:javascript
复制
import { Component, OnInit, ContentChildren, QueryList } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';

import { SortablejsOptions, SORTABLEJS_DIRECTIVES } from 'angular-sortablejs';
import Immutable = require('immutable');

import { ObjectionComponent } from './objection/objection.component';
import { ObjectionModel } from '../objection';
import { ObjectionStore } from '../objection-store';
import { DataService } from '../data.service';
import { addObjection } from '../actions';


@Component({
  moduleId: module.id,
  selector: 'app-list',
  templateUrl: 'list.component.html',
  styleUrls: ['list.component.css'],
  providers: [ObjectionStore, DataService],
  directives: [ObjectionComponent, SORTABLEJS_DIRECTIVES]
})
export class ListComponent implements OnInit {

  private sub: any;
  editable: boolean = false;
  touched: boolean = false;
  expanded: boolean = false;
  options: SortablejsOptions = {
    disabled: false
  };
  objectionID: number;

  constructor(
    private store: ObjectionStore,
    private route: ActivatedRoute) {}

  ...

}

objection-store.ts

代码语言:javascript
复制
import { Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import Immutable = require('immutable');
import { createStore } from 'redux';

import { ObjectionAction } from './actions';
import { reducer } from './reducer';
import { ObjectionModel } from './objection';
import { DataService } from './data.service';

@Injectable()
export class ObjectionStore {
  private sub: any;
  store: any;

  constructor(
    private dataService: DataService) {
           this.store = createStore(reducer, Immutable.List<ObjectionModel>(objections.json()));
    });

  }

data.service.ts

代码语言:javascript
复制
import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';

import { ObjectionModel } from './objection';
import { Area } from './area';

let objectionsPromise;

@Injectable()
export class DataService {
    result: Object;
    combined: any;
    error: Object;
    getUrl: string = 'https://script.google.com/macros/s/AKfycbymzGKzgGkVo4kepy9zKIyDlxbnLbp-ivCvj8mVMClmWgr-V-g/exec?json=1';
    postUrl: string = 'https://script.google.com/macros/s/AKfycbymzGKzgGkVo4kepy9zKIyDlxbnLbp-ivCvj8mVMClmWgr-V-g/exec';

    static getObjection(objections: any[], id: number): ObjectionModel {
        return objections.filter(function(objection) {
            return objection.id === id
        })[0];
    }

    constructor(private http: Http) {
        objectionsPromise = this.http.get(this.getUrl).toPromise();
    }
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-07-29 00:52:40

基本答案

要回答眼前的问题,从服务器获得反对意见并将其放入模板可以简单地这样做:

  1. 使用异步管道将一个可观察到的直接绑定到模板中。异步管道'unboxes‘可观察到(也承诺),并更新您的模板,当它们改变。
代码语言:javascript
复制
<ul id="objection-list"
    [sortablejs]="store.objections"
    [sortablejsOptions]="options"
    (update)="setTouched()">
  <li *ngFor="let objection of objections | async">
    <list-objection 
                    [objection]="objection" 
                    [editable]="editable"
                    (onEdit)="setTouched()" 
                    (onReordered)="setReordered(objection)">
    </list-objection>
  </li>
</ul>
  1. 当您的组件初始化时,使用DataService初始化此可观察到的组件:
代码语言:javascript
复制
@Component({
  moduleId: module.id,
  selector: 'app-list',
  templateUrl: 'list.component.html',
  styleUrls: ['list.component.css'],
  providers: [ObjectionStore, DataService],
  directives: [ObjectionComponent, SORTABLEJS_DIRECTIVES]
})
export class ListComponent implements OnInit {

  // ...

  private objections: Observable<ObjectionModel[]>;

  constructor(
    private dataService: DataService,
    private route: ActivatedRoute) {}

  ngOnInit() {
    this.objections = this.dataService.getObjections();
  }

  // ...
  1. 修复您的数据服务:
代码语言:javascript
复制
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

import { ObjectionModel } from './objection';

@Injectable()
export class DataService {
    result: Object;
    combined: any;
    error: Object;
    getUrl: string = 'https://script.google.com/macros/s/AKfycbymzGKzgGkVo4kepy9zKIyDlxbnLbp-ivCvj8mVMClmWgr-V-g/exec?json=1';
    postUrl: string = 'https://script.google.com/macros/s/AKfycbymzGKzgGkVo4kepy9zKIyDlxbnLbp-ivCvj8mVMClmWgr-V-g/exec';

    getObjections(private http: Http): Observable<ObjectionModel[]> {
        return this.http.get(this.getUrl) // returns an observable of the response
            .map(response => response.json()); // transforms it into an observable of ObjectionModels
    }
}

关于Redux的说明

注意,这一切都是在没有Redux的情况下完成的。

总的来说,我喜欢Redux,我经常使用它。然而,在您的示例中,您似乎正在做一些非常规的事情:

  1. 您正在ObservableStore服务中创建一个商店--这对我来说意味着您正在计划在您的应用程序中有几个商店。Redux的主要原则之一是全局不变状态,这意味着应用程序中通常只有一个Redux存储。
  2. 您可以尝试从服务器获取初始数据集,然后在响应返回时创建存储。通常来说,将存储创建与这样的HTTP请求相结合并不是一个好主意。相反,我建议在初始化应用程序时创建一个空存储,然后在HTTP请求返回时通过还原器更新它。
  3. 您可以在角2中执行原始Redux操作,但是您可能会发现,让它与Angular的可观察到的重API一起工作有点令人沮丧。幸运的是,人们(包括我)已经以可观察的面向对象的redux库(如NG2-剩余ngrx/商店 )的形式为您完成了这项工作。

如果您使用ng2-redux,情况会更像这样:

顶级应用程序组件:构建您的商店并初始化它:

代码语言:javascript
复制
import { NgRedux } from 'ng2-redux';
import { rootReducer } from './reducers';

@Component({ /* ... */ })
class App {
  constructor(private ngRedux: NgRedux<any>) {
    this.ngRedux.configureStore(rootReducer, {});
  }
}

列表组件:将模板绑定到存储区当前数据的选择器。还会在初始化时触发数据获取。

代码语言:javascript
复制
import { NgRedux, select } from 'ng2-redux';

@Component({
  moduleId: module.id,
  selector: 'app-list',
  templateUrl: 'list.component.html',
  styleUrls: ['list.component.css'],
  providers: [DataService],
  directives: [ObjectionComponent, SORTABLEJS_DIRECTIVES]
})
export class ListComponent implements OnInit {

  // ...

  // Magic selector from ng2-redux that makes an observable out
  // of the 'objections' property of your store.
  @select('objections') objections: Observable<ObjectionModel[]>;

  constructor(
      private ngRedux: NgRedux<any>,
      private dataService: DataService) {}

  ngOnInit() {
      this.subscription = this.dataService.getObjections()
         .subscribe(objections => this.ngRedux.dispatch({
             type: FETCH_OBJECTIONS_OK,
             payload: objections
         },
         error => this.ngRedux.dispatch({
             type: FETCH_OBJECTIONS_ERROR,
             error: error
         });
      )
  }
}

好的..。那么这些数据是如何进入商店的呢?通过减速机。记住,在redux存储状态下,只能从减速机更改。

代码语言:javascript
复制
export function objectionReducer(state = [], action) {
    switch(action.type) {
        case FETCH_OBJECTIONS_OK: return [ ...action.payload ];
        case ADD_OBJECTION: return [ ...state, action.payload ];
        // etc.
    }

    return state;
}

我们也可以跟踪一个减速器的错误,如果我们想,你想要如何结构,这取决于你。

代码语言:javascript
复制
export function errorReducer(state = {}, action) {
    switch(action.type) {
        case FETCH_OBJECTIONS_ERROR: return { objectionFetch: action.error }
    }
}

由于我们只有一家商店,所以我们将减速机模块化,并将它们组合在一起:

代码语言:javascript
复制
import { combineReducers } from 'redux';
import { objectionReducer } from './objection.reducer';
import { errorReducer } from './error.reducer';

export const rootReducer = combineReducers({
    objections: objectionReducer,
    error: errorReducer
});

更多地学习/披露

信息披露:我是Ng2-Redux的作者之一。然而,Ngrx/Store也是使用Ng2进行redux的一个很好的选择,虽然实现不同,但使用它与我前面描述的非常相似。

我和我的同事还保留了一些关于Angular2和Redux的培训资源,我将在下面提供这些资源:

票数 2
EN

Stack Overflow用户

发布于 2016-07-22 18:31:00

你的数据服务是错误的。你漏掉了诺言的要点[可观察的]。您应该阅读有关http客户端的角度文档。至少读一下这部分:https://angular.io/docs/ts/latest/guide/server-communication.html#!#promises

构造函数中没有http调用!这更像是:

代码语言:javascript
复制
getHeroes (): Promise<Hero[]> {
  return this.http.get(this.heroesUrl)
                  .toPromise()
                  .catch(this.handleError);
}

在你习惯了这一点之后,我真的建议你读一些关于可观察性的文章。更干净更先进。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38530860

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档