使用异步管道观察不会更新UI Angular 4

时间:2018-04-17 07:19:05

标签: angular asynchronous observable

我将简单的数据集加载到observable中,如下所示:

public tasks: Observable<UserTask[]>;
constructor(private dataService: HttpdataService, private changeDetector: ChangeDetectorRef) { }

ngOnInit() {
  this.loadTasks();
}

loadTasks() {
  this.tasks = this.dataService.loadTasks(someurl);
}

completeTasks(task: UserTask, url: string) {
  this.dataService.finishTasks(url, task.id).then(() => {
     this.toastr.success('Task completed successfully!', 'Success');
         this.tasks.subscribe(() => {
         this.changeDetector.markForCheck();
     });
  }).catch(err => {
    this.toastr.error('The request has errored out!', 'Error');
    console.error(err);
  });
}

我的用户界面看起来像

<tr *ngFor="let task of tasks | async">
   //td data
</tr>
单击按钮将调用

completeTasks()并成功执行但在完成任务操作后,UI永远不会更新。使用ChangeDetectionRef是我最后的选择。我尝试使用ngZone运行该操作,但无法成功。

当我写入this.tasks.subscribe()console.log数据时,我发现该任务已从Observable中删除,但是不是UI更新。我还能尝试什么。有人可以指出我正确的方向。

更新

这是dataservice中的方法:

loadTasks(ep: string): Observable<any> {
    return this.http.get(ep + '/usertask');
}

1 个答案:

答案 0 :(得分:3)

您需要Observable自我管理并专注于task list lifecycle

BehaviorSujectTaskService的附加示例。对于此示例,而是从ajax请求加载,我只在1秒后填充虚拟数据。

然后你有更新/删除动作,他们都必须根据用户的动作更新列表。

Component.ts:

import {
    Component,  OnInit
} from '@angular/core';

import {TaskModel, TaskService} from './services/task.service';


@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
    constructor(public taskService: TaskService) { }

    ngOnInit() {
        this.taskService.fetchTask();
    }

    onChangeHandler(task: TaskModel)
    {
        this.taskService.save(task);
    }

    onDeleteHandler(task: TaskModel) {
        this.taskService.remove(task);
    }
}

这里我们只是根据动作(或生命周期钩子)管理视图和询问服务。在init上我们想从服务器加载,如果复选框正在改变,我们想要更新我们的引用,当我们点击删除按钮时,我们想要从列表中删除(也可以在服务器上)。

<强> component.html

<h2>TODO LIST</h2>
<div *ngFor="let task of (taskService.task$ | async)">
    <p>
        {{ task.title }} | <input type="checkbox" [(ngModel)]="task.status" (change)="onChangeHandler(task)"> | <button (click)="onDeleteHandler(task)"> Delete </button>
    </p>
</div>

现在主要代码在 task.service

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import {isUndefined} from 'util';

export interface TaskModel {
    id: number;
    title: string;
    status: boolean;
}

@Injectable()
export class TaskService {
    /**
     * Observable who should always be the replica of last tasks state.
     */
    private _tasks$: BehaviorSubject<TaskModel[]>;
    /**
     * array of task, use for each action done on task.
     */
    private tasks: TaskModel[];

    constructor() {
        // We init by empty array.
        this._tasks$ = new BehaviorSubject([]);
        this.tasks = [];
    }

    /**
     * Fake fetch data from server.
     */
    fetchTask()
    {
        // Fake request.
        Observable.create(obs => {
            /**
             * After 1 secs, we update internal array and observable.
             */
            setTimeout(() => {
                this.tasks = this.getDummyData();
                obs.next(this.tasks);
            }, 1000);

        }).subscribe(state => this._tasks$.next(state));
    }

    /**
     * Magic getter
     * @return {Observable<{id: number; title: string; status: boolean}[]>}
     */
    get task$(): Observable<TaskModel[]> {
        // return only observable, don't put public your BehaviorSubject
        return this._tasks$.asObservable();
    }

    /**
     * We update from internal array reference, and we next fresh data with our observable.
     * @param {TaskModel} task
     */
    save(task: TaskModel) {
        const index = this.tasks.findIndex(item => item.id === task.id);
        if(!isUndefined(this.tasks[index]))
        {
            this.tasks[index] = task;
        }

        // Notify rest of application.
        this._tasks$.next(this.tasks);
    }

    /**
     * We remove from internal array reference, and we next data with our observable.
     * @param {TaskModel} task
     */
    remove(task: TaskModel) {
        this.tasks = this.tasks.filter(item => item.id !== task.id);
        this._tasks$.next(this.tasks);
    }

    /**
     * Fake data.
     * @return {{id: number; title: string; status: boolean}[]}
     */
    private getDummyData() : TaskModel[]
    {
        return [
            {
                id: 1,
                title: 'task1',
                status: true
            },
            {
                id: 2,
                title: 'task2',
                status: false
            },
            {
                id: 3,
                title: 'task3',
                status: true
            }
        ];
    }
}

Online code