У меня есть простой набор данных, загруженных в 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 () будет вызываться при нажатии кнопки и выполняется успешно, но пользовательский интерфейс никогда не обновляется после завершения операции задачи. Использование ChangeDetectionRef было моим последним вариантом, который я попробовал. Я попытался запустить операцию с помощью ngZone, но не смог.
Когда я записываю данные this.tasks.subscribe() и console.info, я вижу, что эта задача была удалена из Observable, но не обновление пользовательского интерфейса. Что еще могу попробовать. Может ли кто-нибудь указать мне правильное направление.
Обновлять
Вот метод в dataservice:
loadTasks(ep: string): Observable<any> {
return this.http.get(ep + '/usertask');
}
вы используете ChangeDetection.OnPush?
Обнаружение @TomaszKula OnPush является наградой от observable.next. Я подозреваю, что loadTasks вызывает простой вызов API из HttpClient. Если да, то после первого следующего, никогда не обновляется при изменении задач.
@ Yanis-git Да, это простой запрос HttpClient. Обновил свой пост.





вам нужен Observable с самостоятельным управлением и выделенный для task list lifecycle.
Прикрепленный пример с BehaviorSuject и TaskService. В этом примере вместо загрузки из запроса 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);
}
}
Здесь мы просто управляем просмотром и запрашиваем сервис в соответствии с действием (или ловушкой жизненного цикла). При инициализации мы хотим выполнить загрузку с сервера, затем, если флажок меняется, мы хотим обновить нашу ссылку, когда мы нажимаем кнопку удаления, мы хотим удалить из списка (а также, возможно, на сервере).
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
}
];
}
}
Потрясающий Янис .. Спасибо за такое красивое объяснение .. :)
Пожалуйста, @GuruprasadRao, удачного кодирования :)
привет, не могли бы вы показать нам код
this.dataService.loadTasks(someurl)?