我用angular-cli,Immutable和Redux创建了一个应用程序。我按照this文章中的说明进行操作,该文章没有描述如何获取数据。我的应用程序需要使用来自异步http调用的数据初始化Redux存储。
我有一个用于列出具有模板的数据的组件。该组件从商店获取其数据,该数据取决于进行http调用的服务。 http调用有效,但应用程序抛出一个异常,指示列表组件在数据到达之前尝试获取数据。
我的存储库是here
该应用的演示是here
错误讯息:
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
...
<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
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) {}
...
}
异议-store.ts
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
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();
}
答案 0 :(得分:2)
基本答案
要回答当前的问题,从服务器获取异议并将它们放入模板可以像这样简单地完成:
<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>
@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();
}
// ...
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并且我经常使用它。但是在你的例子中,你似乎做了几件非正统的事情:
您正在ObservableStore服务中创建商店 - 这告诉我您计划在您的应用中拥有多个商店。 Redux的主要原则之一是全局不可变状态,这意味着应用程序中通常只有一个Redux存储。
您似乎试图从服务器获取初始数据集,然后在响应返回时创建商店。将商店创建与这样的HTTP请求结合起来通常不是一个好主意。相反,我建议您在初始化应用程序时创建一个空存储,然后在HTTP请求返回时通过reducer更新它。
您可以在Angular 2中执行原始Redux,但是您可能会发现使用Angular的可观察量较大的API有点令人沮丧。幸运的是,人们(包括我)以面向观察者的redux库的形式为你完成了这项工作,例如ng2-redux和ngrx/store
如果您使用ng2-redux,事情会更像是这样:
顶级应用组件:构建商店并对其进行初始化:
import { NgRedux } from 'ng2-redux';
import { rootReducer } from './reducers';
@Component({ /* ... */ })
class App {
constructor(private ngRedux: NgRedux<any>) {
this.ngRedux.configureStore(rootReducer, {});
}
}
列表组件:将模板绑定到商店当前数据的选择器。还会在初始化时触发数据提取。
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存储状态中,只能从reducer更改。
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;
}
如果我们想要,我们也可以跟踪减速器中的错误,您希望如何构建这个错误取决于您。
export function errorReducer(state = {}, action) {
switch(action.type) {
case FETCH_OBJECTIONS_ERROR: return { objectionFetch: action.error }
}
}
由于我们有一个商店,我们将模块化减速器并将它们组合在一起:
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上保留了一些培训资源,我将在下面提供:
答案 1 :(得分:1)
您的数据服务有误。你错过了承诺/可观察的观点。 你应该阅读有关http客户端的角度文档。至少阅读这部分内容: https://angular.io/docs/ts/latest/guide/server-communication.html#!#promises
构造函数中没有http调用! 这更像是:
getHeroes (): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.catch(this.handleError);
}
在你习惯了之后,我真的建议你阅读一些关于Observables的内容。更清洁,更先进。