开发者问题收集

如何让组件在按钮点击时自行删除

2019-01-09
16283

我无法使用 angular 让组件自行删除。

我目前正在学习 angular,并开始了一个小型问候项目。应用程序应如何工作:

  1. 输入您的姓名
  2. 创建了向您致意的子组件。
  3. 子组件包含用于自行删除的按钮

目前,我完成了前两个步骤,一切正常。但我不知道如何让子组件自行删除。从 React 开始,我知道有可能以某种方式使用生命周期方法删除“组件”。angular 中有类似的东西吗?目前我找不到它,但我找到了在组件被销毁之前调用的方法“OnDestroy()”。但我该如何正确销毁它?

父级:

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

@Component({
  selector: 'app-greeter-service',
  templateUrl: './greeter-service.component.html'
})
export class GreeterServiceComponent implements OnInit {
  title = '';
  currentUser = '';
  isVisible = false;

  currentUsers: any[] = [];

  @ViewChild('newUser') inputField;

  constructor() {}

  greetingFunc(newUser : string) {
      if(newUser) {
      this.currentUsers.push(newUser);
      console.log(this.currentUsers);
      this.inputField.nativeElement.value='';
    }
  }

  ngOnInit() {
      this.title = 'Welcome to the Greeter!';
  }

}

子级:

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

@Component({
  selector: 'app-was-greeted',
  templateUrl: './was-greeted.component.html',
  styleUrls: ['./was-greeted.component.scss']
})

export class WasGreetedComponent implements OnInit {
  @Input() user: string;

  constructor() { }

  deleteMe() {
    console.log("here should be the action");
  }

  ngOnInit() {
  }
}

如何“动态”地向应用程序添加组件:

<div class="column" *ngFor="let user of currentUsers">
    <app-was-greeted [user]="user"></app-was-greeted>
</div>

因此,对于数组“currentUsers”中的每个“推送”,都会创建一个组件。

3个回答

正如 @cgTag 所评论的,有很多方法可以解决这个问题。一种方法是将 @Output 添加到您的 WasGreetedComponent ,它将发送到父组件。

然后在您的 GreeterServiceComponent 中,您可以在数组中找到该元素并将其删除(请记住,您的数组应该是不可变的,因此您需要创建该数组的新实例),这将导致 ngFor 重新评估并更新视图

@Component({
  selector: 'app-was-greeted',
  templateUrl: './was-greeted.component.html',
  styleUrls: ['./was-greeted.component.scss']
})

export class WasGreetedComponent implements OnInit {
  @Input() user: string;

  @Output() delete: EventEmitter<string> = new EventEmitter();

  constructor() { }

  deleteMe() {
    console.log("here should be the action");
    this.delete.emit(user);
  }
}

您的父组件模板将订阅此发射器

<div class="column" *ngFor="let user of currentUsers">
    <app-was-greeted [user]="user" (delete)="deleteUser($event)"></app-was-greeted>
</div>

并且该组件将需要处理 deleteUser 回调并从数组中删除用户

@Component({
  selector: 'app-greeter-service',
  templateUrl: './greeter-service.component.html'
})
export class GreeterServiceComponent implements OnInit {
  title = '';
  currentUser = '';
  isVisible = false;

  currentUsers: any[] = [];

  @ViewChild('newUser') inputField;

  constructor() {}

  ...

  deleteUser(user: string) {
    this.currentUsers = this.currentUsers.filter(x => x !== user);
  }

}

就像我说的,这只是众多方法之一。希望这能有所帮助。

Neil Stevens
2019-01-09

I can't make a component delete itself with angular.

假设渲染的 HTML 表示应用程序的 当前 状态。有一种状态是组件存在,还有一种状态是组件不存在。不要将其视为 删除 的行为,而应认为 状态 已发生改变,之后组件不再呈现。

  1. Type your name

状态 没有任何问候。

  1. Child component is created that is greeting you.

状态 至少有一个获取。

  1. Child component contains button to delete itself

状态 恢复为没有任何问候。

Currently i fulfilled the first two steps and everything works fine. But i have no idea how i can make the child component delete itself. Coming from React i know, that there was the possibility to delete a "component" with the lifecycle methods somehow.

如果您强制 删除 该组件,则页面上的组件将不再代表应用程序的当前 状态 。如果您有 微状态 ,这种情况是可以接受的,例如模态对话框。对话框本身具有自己的状态,并且对话框的显示具有相对于该微状态的生命周期。

根据您给出的示例,我认为情况并非如此。

currentUsers: any[] = [];

以上是您的 state 变量。该 array 的值表示将在 HTML 中呈现的内容。您可以提出一些问题,答案将帮助您。

  1. 谁拥有该状态?
  2. 谁可以改变该状态?
  3. 我如何要求状态所有者更改它?

让我们回答这些问题

  1. who owns the state?

GreeterServiceComponent 组件是状态的所有者,在本例中,状态表示为一个数组。在 Web 组件中,我们将其称为组件的状态。这是组件的内部事物。

  1. who can mutate that state?

我们只希望 GreeterServiceComponent 对此状态进行更改。类之外的源代码不应直接访问和改变它。我们可以说组件处理此内部状态的存储和生命周期。

  1. how do I ask the state owner to change it?

这就是我们进入 Angular 细节的地方。在 Angular 中,我们有多种方式在组件之间进行通信。在这种情况下,我们希望 组件与 组件通信,告知它应该 更改 其内部状态。我们想告诉父组件不要再显示问候语。解决这个问题的每种方法都有其优点和缺点,由您决定哪种方法适合您。

@Output() 绑定

Angular 中的组件可以具有 @Output() ,用于执行父组件模板中的 表达式 。这与将 回调 函数传递给 React 组件的属性相同。

WasGreetedComponent 中,您将添加:

@Output()
public closed: Subject<void>() = new Subject();

deleteMe() { this.closed.next(); }

GreeterServiceComponent 模板中,您将更改:

<div class="column" *ngFor="let user of currentUsers">
    <app-was-greeted [user]="user" 
                     (closed)="currentUsers = currentUsers.filter(u=>u!==user)">
    </app-was-greeted>
</div>

父注入

Angular 中的子组件可以通过构造函数注入其自己的父组件,然后您可以直接通知父组件。

此方法绕过模板,父级中所做的任何更改都可能需要更新视图。因此,建议父级使用 ChangeDetectorRef 将其视图标记为 dity。

GreeterServiceComponent 中,您将添加:

 public deleteUser(user: any) {
      this.currentUsers = this.currentUsers.filter(u=>u !== user);
      this.changeDetectorRef.markForCheck();
 }

WasGreetedComponent 中,您将添加:

 constructor(parent: GreeterServiceComponent) {}
 deleteMe() { this.parent.deleteUser(this.user); }

通过反应式编程实现全局状态

最后一种方法使用 反应式编程 ,其中使用 可观察对象 ,允许 消费者 监视应用程序状态的变化。这样做的好处是 状态 外部 服务 拥有 管理 。 Angular/React/Vue 框架中大量使用 Redux/NGRX/NGXS 等流行服务。使用状态存储有很多优势,但这些框架 难以 掌握,对于小型项目来说,这往往是大材小用。一旦开始使用它们,就很难停止使用。

我们可以创建自己的小版本作为演示。

您将添加一个服务来表示您的应用程序状态。

 @Injectable({provideIn: 'root'})
 export class StateService {
      public users: BehaviorSubject<any[]> = new BehaviorSubject([]);

      public addUser(user: any) {
           this.users
             .pipe(first())
             .subject(users => this.users.next([...users, user]));
      }

      public removeUser(user: any) {
           this.users
              .pipe(first())
              .subject(users => this.users.next(users.filter(u=>u !== user)));
      }
 }

现在,在您的 GreeterServiceComponent 中,它将不再是状态的 所有者 。我们将注入上述服务并允许该服务对其进行管理。

@Component({...})
export class GreeterServiceComponent implements OnInit {
  constructor(public state: StateService) {}
  greetingFunc(newUser : string) {
      if(newUser) {
          this.state.addUser(newUser);
      }
}

GreeterServiceComponent 模板中,我们将使用 async 管道直接从 StateService 显示用户的当前 状态 。我们这样做是因为该服务掌握了当前状态的 真实 信息。 GreeterServiceComponent 组件不会关心它如何变化,因为它不拥有或管理它。

<div class="column" *ngFor="let user of state.users | async">
    <app-was-greeted [user]="user"></app-was-greeted>
</div>

WasGreetedComponent 组件将使用 StateService 更改当前 状态 。此组件不关心其父组件。您可以在应用程序中 移动 此组件,它仍将正常运行。这是一个重要的好处,因为上述其他方法都依赖于 DOM 本身的结构。因此,移动组件需要对其他地方进行更改才能使组件保持正常工作。

@Component({...})
export class WasGreetedComponent implements OnInit {
  constructor(public state: StateService) {}
  deleteMe() {
     this.state.removeUser(this.user);
  }
Reactgular
2019-01-09

让组件具有父级监听的 @Output 参数并将其从 currentUsers 中移除,或者为 *ngFor 添加额外的逻辑,以便在循环中绘制元素时忽略它。可能像这样?

<div class="column" *ngFor="let user of currentUsers; let i = index">
<app-was-greeted *ngIf="shouldDisplay(i)" (buttonClick)="removeMe(i)" [user]="user"> 
</app-was-greeted>
</div>
Stol
2019-01-09