如何让组件在按钮点击时自行删除
我无法使用 angular 让组件自行删除。
我目前正在学习 angular,并开始了一个小型问候项目。应用程序应如何工作:
- 输入您的姓名
- 创建了向您致意的子组件。
- 子组件包含用于自行删除的按钮
目前,我完成了前两个步骤,一切正常。但我不知道如何让子组件自行删除。从 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”中的每个“推送”,都会创建一个组件。
正如 @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);
}
}
就像我说的,这只是众多方法之一。希望这能有所帮助。
I can't make a component delete itself with angular.
假设渲染的 HTML 表示应用程序的 当前 状态。有一种状态是组件存在,还有一种状态是组件不存在。不要将其视为 删除 的行为,而应认为 状态 已发生改变,之后组件不再呈现。
- Type your name
状态 没有任何问候。
- Child component is created that is greeting you.
状态 至少有一个获取。
- 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 中呈现的内容。您可以提出一些问题,答案将帮助您。
- 谁拥有该状态?
- 谁可以改变该状态?
- 我如何要求状态所有者更改它?
让我们回答这些问题
- who owns the state?
GreeterServiceComponent
组件是状态的所有者,在本例中,状态表示为一个数组。在 Web 组件中,我们将其称为组件的状态。这是组件的内部事物。
- who can mutate that state?
我们只希望
GreeterServiceComponent
对此状态进行更改。类之外的源代码不应直接访问和改变它。我们可以说组件处理此内部状态的存储和生命周期。
- 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);
}
让组件具有父级监听的 @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>