【问题标题】:Angular deleting all completed not working without refresh角删除所有完成不刷新不工作
【发布时间】:2018-11-25 19:28:58
【问题描述】:

我是 Angular 的新手,我正在创建一个 CRUD 待办事项应用程序,其中待办事项存储到 localStorage。 添加,更新,删除,让所有工作正常,但如果我删除所有已完成的待办事项,它仅在刷新时有效,这意味着单击它会从 localStorage 中删除,但不会从屏幕上删除

这是我的项目文件

app.component.html

<div class="container">
  <div class="todo-wrapper">
    <app-todo-input></app-todo-input>
    <div *ngFor="let todo of allTodos">
        <app-todo-item [todo]="todo"></app-todo-item>
    </div>
    <app-todo-footer [style.display]="allTodos.length <= 0 ? 'none': 'inline'"></app-todo-footer>
  </div>
</div>

app.component.ts

import { Component } from '@angular/core';
import { TodoService } from './todo.service';
import { StorageService } from './storage.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
    public allTodos = [];
    constructor(private todoService: TodoService, private storageService: StorageService){
    };

    ngOnInit(){
        this.todoService.getTodos().subscribe(todos => this.allTodos = todos);
        // this.storageService.getTodos().subscribe(todos => this.allTodos = todos);
    }
}

tod​​o.ts

export class Todo {
    id: number;
    text: string;
    completed: boolean;

    constructor(id: number, text: string, completed: boolean){
        this.id = id;
        this.text = text;
        this.completed = completed;
    }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {MatButtonModule, MatCheckboxModule} from '@angular/material';

import { AppComponent } from './app.component';
import { TodoInputComponent } from './todo-input/todo-input.component';
import { TodoService } from './todo.service';
import { StorageService } from './storage.service';
import { TodoItemComponent } from './todo-item/todo-item.component';
import { TodoFooterComponent } from './todo-footer/todo-footer.component';
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  declarations: [
    AppComponent,
    TodoInputComponent,
    TodoItemComponent,
    TodoFooterComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    BrowserAnimationsModule,
    MatButtonModule,
    MatCheckboxModule,
    AppRoutingModule
  ],
  providers: [
    TodoService,
    StorageService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

storage.service.ts

import { Injectable } from '@angular/core';
import { Todo } from './todo';
import { Observable, of, BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class StorageService {

  constructor() { }

  public setTodos(todos: Todo[]): void {
    localStorage.setItem('todos', JSON.stringify({ todos: todos }))
  }

  public getTodos(): Observable<Todo[]>{
    let localStorageItem = JSON.parse(localStorage.getItem('todos'));
    if(localStorageItem == null){
        return of([]);
    }else{
        return of(localStorageItem.todos);
    }
  }
}

tod​​o.service.ts

import { Injectable, Input } from '@angular/core';
import { StorageService } from './storage.service';
import { Observable, of, BehaviorSubject } from 'rxjs';

import { Todo } from './todo';

@Injectable({
  providedIn: 'root'
})
export class TodoService {

  @Input()
  private todo: Todo;

    public allTodos: Todo[] = [];
    private nextId: number; 

  constructor(private storageService: StorageService) {
    this.loadTodos();
  }  

  public addTodo(text: string) : void{
    let todos = this.allTodos;
    if (todos.length == 0) { 
      this.nextId = 0;
    } else {
      let maxId = todos[todos.length - 1].id;
      this.nextId = maxId + 1;
    }
    let todo = new Todo(this.nextId, text, false);
    todos.push(todo);
    this.storageService.setTodos(todos);
    this.nextId++;
    // this.lengthTodos();
  }

  // public getTodos() {
  //    return this.allTodos;
  // }

  public loadTodos (){
    return this.storageService.getTodos().subscribe(todos => this.allTodos = todos);
  }

  public getTodos(): Observable<Todo[]>{
    return of(this.allTodos)
  }

  public removeTodo(selectedTodo): void{
    let todos = this.allTodos;
    todos.splice(todos.findIndex((todo) => todo.id == selectedTodo), 1);
    this.storageService.setTodos(todos);
  }

  public deleteCompleted(){
    let todos = this.allTodos;
    let completedTodos = todos.filter(todo => todo.completed !== true);
    todos = completedTodos;
    this.storageService.setTodos(todos);
  }

  public update(id, newValue){
    let todos = this.allTodos;
    let todoToUpdate = todos.find((todo) => todo.id == id);
    todoToUpdate.text = newValue;
    this.storageService.setTodos(todos);
  } 

  public isCompleted(id: number, completed: boolean){
    let todos = this.allTodos;
    let todoToComplete = todos.find((todo) => todo.id == id);
    todoToComplete.completed = !todoToComplete.completed;
    this.storageService.setTodos(todos);
  }

  // public lengthTodos(){
  //   let todos = this.storageService.getTodos();
  //   let activeTodos = todos.filter((todo) => !todo.completed).length;
  //   return activeTodos;
  // }
}

tod​​o-footer.component.ts

    import { Component, OnInit, Input } from '@angular/core';
    import { TodoService } from '../todo.service';
    import { Todo } from '../todo';

    @Component({
      selector: 'app-todo-footer',
      templateUrl: './todo-footer.component.html',
      styleUrls: ['./todo-footer.component.css']
    })
    export cl

ass TodoFooterComponent implements OnInit {

  public allTodos;

    private activeTasks : number = 0;

  constructor(private todoService: TodoService) {
    this.todoService.getTodos().subscribe(todos => this.allTodos = todos);
  }

  public getLength(){
    // this.activeTasks = this.todoService.lengthTodos();
  }

  private clearCompleted(){
    this.todoService.deleteCompleted(); 
  }

  ngOnInit() {
    // this.getLength();
  }

}

tod​​o-footer.component.html

<footer class="footer">

    <button class="clear-completed" (click)="clearCompleted()">Clear completed</button> 
</footer>

在 todo.service 中,deleteCompleted 方法不起作用,由 todo-footer.component 用于删除所有已完成的 todos==true。

【问题讨论】:

  • 您可以将代码添加到 StackBlitz 中并分享链接吗?顺便说一句,发生这种情况的原因是因为splice 修改了保存在内存中的数组,而filter 返回了一个新数组,并且保持现有数组不变。尽管您使用的是 Observable,但当列表更改时,您不会发出任何新值,
  • 不应该是todo.completed === true吗?
  • stackblitz.com/edit/angular-kxjnyh 这是 stacknlitz 链接。 @user184994
  • 谢谢@FatemeFazli 我也试过了,但这不是问题,相反我不知道如何在这里使用拼接

标签: javascript angular typescript rxjs observable


【解决方案1】:

你在这里有一个 Observable,但你实际上并没有使用它。相反,您将数组的单个实例保存在多个组件中,并依赖于该实例被修改,但是filter 不会修改内存中的实例:它返回一个新实例。

创建一个BehaviourSubject,像这样:

private _todos = new BehaviorSubject<Todo[]>(null);

我们可以根据需要使用它来更新 Observable 值。

像这样更改getTodos 函数:

public getTodos(): Observable<Todo[]>{
  return this._todos.asObservable();
}

现在,每当您更改todos 的值时,您都应该在BehaviourSubject 上调用next。例如:

public deleteCompleted(){
  let todos = this.allTodos;
  let completedTodos = todos.filter(todo => todo.completed === false);
  this.allTodos = completedTodos;
  this.storageService.setTodos(this.allTodos);
  // Emit the new value from the Observable
  this._todos.next(this.allTodos);
}

Here is a fork of your Stackblitz to show it working

【讨论】:

  • 非常感谢@user184994 的大力支持
  • 互联网上是否有任何文档可以彻底研究 observables 和 BehaviorSubject?
  • 加载。如果您转到angular.io/docs 并在搜索栏中搜索Observable,它应该将您链接到4 篇不同的文章。此外,应该有大量可通过 Google 访问的材料
猜你喜欢
  • 2014-11-10
  • 1970-01-01
  • 2016-05-17
  • 1970-01-01
  • 2018-10-11
  • 1970-01-01
  • 1970-01-01
  • 2019-05-01
  • 1970-01-01
相关资源
最近更新 更多