Observable с помощью async pipe не обновляет пользовательский интерфейс Angular 4

У меня есть простой набор данных, загруженных в 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');
}

привет, не могли бы вы показать нам код this.dataService.loadTasks(someurl)?

Yanis-git 17.04.2018 09:24

вы используете ChangeDetection.OnPush?

Tomasz Kula 17.04.2018 09:26

Обнаружение @TomaszKula OnPush является наградой от observable.next. Я подозреваю, что loadTasks вызывает простой вызов API из HttpClient. Если да, то после первого следующего, никогда не обновляется при изменении задач.

Yanis-git 17.04.2018 09:28

@ Yanis-git Да, это простой запрос HttpClient. Обновил свой пост.

Guruprasad J Rao 17.04.2018 09:59
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
4
4 411
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

вам нужен 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
            }
        ];
    }
}

Онлайн-код

Потрясающий Янис .. Спасибо за такое красивое объяснение .. :)

Guruprasad J Rao 17.04.2018 10:47

Пожалуйста, @GuruprasadRao, удачного кодирования :)

Yanis-git 17.04.2018 11:24

Другие вопросы по теме