与孩子们在Angular中的通信

huangapple go评论65阅读模式
英文:

Communication between children in angular

问题

我正在制作一个学生列表。输入字段、添加按钮和更新按钮都在一个子组件中,而在另一个子组件中有包含删除和编辑按钮的列表。这两者都在父组件中处理。

当我点击编辑按钮时,我希望输入字段中有来自列表的值,并能够更新列表。

父组件 HTML

  <ul>
    <app-list-item
      *ngFor="let studentName of students; let i = index"
      [name]="studentName"
      [index]="i"
      (deleteNameByIndex)="onemitDeleteNameByIndex($event)"
      (editNameById)="onemitEditNameById($event)"
    >
  </ul>

父组件 .ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  students = ['huzaifa', 'hunzila', 'waqar', 'sibte', 'shahzeen'];
  student = '';

  onemitAddNewName(newName: string) {
    this.students.push(newName);
  }
  onemitDeleteNameByIndex(index: number) {
    this.students.splice(index, 1);
  }
  onemitEditNameById(student: any) {
    this.student = student;
    console.log('app student: >> ', this.student);
  }
}

子组件1 HTML

<input
  type="text"
  placeholder="enter name"
  [value]="name"
  (input)="oninputSetName($event)"
/>
{{ student }}
{{ name }}

<button (click)="onclickEmitNewName()">Add</button>
<button>Update</button>

子组件1 .ts

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

@Component({
  selector: 'app-list-form',
  templateUrl: './list-form.component.html',
  styleUrls: ['./list-form.component.css'],
})
export class ListFormComponent {
  name = '';
  @Output() newName = new EventEmitter<string>();
  @Input() student = '';

  oninputSetName(event: any) {
    this.name = event.target.value;
  }

  onclickEmitNewName() {
    this.newName.emit(this.name);
  }
  updateInput() {
    let obj = { name: this.student };
    console.log('list-form student: >> ', this.student);
  }
}

子组件2 HTML

{{ name }} -- {{ index }}
<button (click)="onclickDeleteName()">delete</button>
<button (click)="onclickEditName()">edit</button>

子组件2 .ts

@Component({
  selector: 'app-list-item',
  templateUrl: './list-item.component.html',
  styleUrls: ['./list-item.component.css'],
})
export class ListItemComponent {
  @Input() name = '';
  @Input() index = 0;

  @Output() deleteNameByIndex = new EventEmitter<number>();
  @Output() editNameById = new EventEmitter<any>();

  onclickDeleteName() {
    this.deleteNameByIndex.emit(this.index);
  }
  onclickEditName() {
    let obj = { index: this.index, name: this.name };
    this.editNameById.emit(obj);
  }
}
英文:

I am making a list of students. The input field, add button, and update button are inside one child and in other child there is the list with the delete and edit buttons. Both are handled in the parent component.

When I click edit button, I would like the input filed to have a value from that list and to be able to update the list.

parent html

  &lt;ul&gt;
    &lt;app-list-item
      *ngFor=&quot;let studentName of students; let i = index&quot;
      [name]=&quot;studentName&quot;
      [index]=&quot;i&quot;
      (deleteNameByIndex)=&quot;onemitDeleteNameByIndex($event)&quot;
      (editNameById)=&quot;onemitEditNameById($event)&quot;
    &gt;
  &lt;/ul&gt;

parent .ts

import { Component } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-root&#39;,
  templateUrl: &#39;./app.component.html&#39;,
  styleUrls: [&#39;./app.component.css&#39;],
})
export class AppComponent {
  students = [&#39;huzaifa&#39;, &#39;hunzila&#39;, &#39;waqar&#39;, &#39;sibte&#39;, &#39;shahzeen&#39;];
  student = &#39;&#39;;

  onemitAddNewName(newName: string) {
    this.students.push(newName);
  }
  onemitDeleteNameByIndex(index: number) {
    this.students.splice(index, 1);
  }
  onemitEditNameById(student: any) {
    this.student = student;
    console.log(&#39;app student :&gt;&gt; &#39;, this.student);
  }
}

child 1 html

&lt;input
  type=&quot;text&quot;
  placeholder=&quot;enter name&quot;
  [value]=&quot;name&quot;
  (input)=&quot;oninputSetName($event)&quot;
/&gt;
{{ student }}
{{ name }}

&lt;button (click)=&quot;onclickEmitNewName()&quot;&gt;Add&lt;/button&gt;
&lt;button&gt;Update&lt;/button&gt;

child 1 .ts

import { Component, EventEmitter, Input, Output } from &#39;@angular/core&#39;;

@Component({
  selector: &#39;app-list-form&#39;,
  templateUrl: &#39;./list-form.component.html&#39;,
  styleUrls: [&#39;./list-form.component.css&#39;],
})
export class ListFormComponent {
  name = &#39;&#39;;
  @Output() newName = new EventEmitter&lt;string&gt;();
  @Input() student = &#39;&#39;;

  oninputSetName(event: any) {
    this.name = event.target.value;
  }

  onclickEmitNewName() {
    this.newName.emit(this.name);
  }
  updateInput() {
    let obj = { name: this.student };
    console.log(&#39;list-form student :&gt;&gt; &#39;, this.student);
  }
}

child 2 html

{{ name }} -- {{ index }}
&lt;button (click)=&quot;onclickDeleteName()&quot;&gt;delete&lt;/button&gt;
&lt;button (click)=&quot;onclickEditName()&quot;&gt;edit&lt;/button&gt;

child 2 .ts

@Component({
  selector: &#39;app-list-item&#39;,
  templateUrl: &#39;./list-item.component.html&#39;,
  styleUrls: [&#39;./list-item.component.css&#39;],
})
export class ListItemComponent {
  @Input() name = &#39;&#39;;
  @Input() index = 0;

  @Output() deleteNameByIndex = new EventEmitter&lt;number&gt;();
  @Output() editNameById = new EventEmitter&lt;any&gt;();

  onclickDeleteName() {
    this.deleteNameByIndex.emit(this.index);
  }
  onclickEditName() {
    let obj = { index: this.index, name: this.name };
    this.editNameById.emit(obj);
  }
}

答案1

得分: 1

甚至更加优雅的方式是,您可以使用一个辅助服务来解决您的通讯问题。

下面是一个示例服务:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  private messageSource = new Subject<string>();
  currentMessage = this.messageSource.asObservable();

  constructor() {}

  changeMessage(message: string) {
    this.messageSource.next(message);
  }
}
import { Component } from '@angular/core';
import { MessageService } from './message.service';

@Component({
  selector: 'app-sender',
  template: `
    <button (click)="sendMessage()">Send Message</button>
  `
})
export class SenderComponent {
  constructor(private messageService: MessageService) {}

  sendMessage() {
    this.messageService.changeMessage('Hello from Sender Component');
  }
}
import { Component } from '@angular/core';
import { MessageService } from './message.service';

@Component({
  selector: 'app-receiver',
  template: `
    <p>{{ message }}</p>
  `
})
export class ReceiverComponent {
  message: string;

  constructor(private messageService: MessageService) {
    this.messageService.currentMessage.subscribe(message => {
      this.message = message;
    });
  }
}
英文:

Or even in a more elegant way, you could use a helper service to solve your communication issue.

Below you could find a sample service:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

import { Injectable } from &#39;@angular/core&#39;;
import { Subject } from &#39;rxjs&#39;;

@Injectable({
  providedIn: &#39;root&#39;
})
export class MessageService {
  private messageSource = new Subject&lt;string&gt;();
  currentMessage = this.messageSource.asObservable();

  constructor() {}

  changeMessage(message: string) {
    this.messageSource.next(message);
  }
}

<!-- end snippet -->

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

import { Component } from &#39;@angular/core&#39;;
import { MessageService } from &#39;./message.service&#39;;

@Component({
  selector: &#39;app-sender&#39;,
  template: `
    &lt;button (click)=&quot;sendMessage()&quot;&gt;Send Message&lt;/button&gt;
  `
})
export class SenderComponent {
  constructor(private messageService: MessageService) {}

  sendMessage() {
    this.messageService.changeMessage(&#39;Hello from Sender Component&#39;);
  }
}

<!-- end snippet -->

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

import { Component } from &#39;@angular/core&#39;;
import { MessageService } from &#39;./message.service&#39;;

@Component({
  selector: &#39;app-receiver&#39;,
  template: `
    &lt;p&gt;{{ message }}&lt;/p&gt;
  `
})
export class ReceiverComponent {
  message: string;

  constructor(private messageService: MessageService) {
    this.messageService.currentMessage.subscribe(message =&gt; {
      this.message = message;
    });
  }
}

<!-- end snippet -->

答案2

得分: 0

由于 Angular 是按引用传递的。您可以利用这一点。当这样做时,甚至无需发出更改的值。

例如,在您的代码中 `Child 1` 处:
与其发出一个本地变量,您可以将值分配给 `@Input`。

这里是一个示例:
@Input existingStudentName: string;
localName: string = existingStudentName;

onUserUpdate(){
   existingStudentName = localName;
   // 无需发出,因为这里已更新 existingStudentName
   // 它将在引用它的父级或其他任何地方更新
}

<input type="text" [(ngModel)]="localName">
<input type="button" (click)="onUserUpdate()">
英文:

Since angular is Pass By reference. You can take advantage of that. when you do that you don't even have to emit the changed value.

For Example, In your code at Child 1:
Rather than emitting a local variable. All you can do is assign the value to @Input.

Here is a example:

@Input existingStudentName: string;
localName: string = existingStudentName;

onUserUpdate(){
   existingStudentName = localName;
   //No need to emmit since existingStudentName is updated here
   //It will update in parent or anyone who refer it
}
&lt;input type=&quot;text&quot; [(ng-Model)]=&quot;localName&quot;&gt;
&lt;input type=&quot;button&quot; (click)=&quot;onUserUpdate()&quot;&gt;

答案3

得分: -1

父级 HTML

    &lt;section&gt;
      &lt;!-- 输入字段,我在其中输入数据 --&gt;
      &lt;app-list-form
        (newName)=&quot;onemitAddNewName($event)&quot;
        [student]=&quot;student&quot;
        (updatedName)=&quot;updateThisNameInList($event)&quot;
      &gt;&lt;/app-list-form&gt;
    
      &lt;!-- 渲染我的列表 --&gt;
      &lt;ul&gt;
        &lt;app-list-item
          *ngFor=&quot;let studentName of students; let i = index&quot;
          [name]=&quot;studentName&quot;
          [index]=&quot;i&quot;
          (deleteNameByIndex)=&quot;onemitDeleteNameByIndex($event)&quot;
          (editNameById)=&quot;onemitEditNameById($event)&quot;
        &gt;&lt;/app-list-item&gt;
      &lt;/ul&gt;
    &lt;/section&gt;

父级 TypeScript

    import { Component } from &#39;@angular/core&#39;;
    
    @Component({
      selector: &#39;app-root&#39;,
      templateUrl: &#39;./app.component.html&#39;,
      styleUrls: [&#39;./app.component.css&#39;],
    })
    export class AppComponent {
      students = [&#39;huzaifa&#39;, &#39;hunzila&#39;, &#39;waqar&#39;, &#39;sibte&#39;, &#39;shahzeen&#39;];
      student = null;
    
      onemitAddNewName(newName: string) {
        this.students.push(newName);
      }
    
      onemitDeleteNameByIndex(index: number) {
        this.students.splice(index, 1);
      }
      onemitEditNameById(student: any) {
        this.student = student;
      }
    
      updateThisNameInList(student: any) {
        let newName = student.name;
        let index = student.index;
    
        this.students.splice(index, 1, newName);
      }
    }

子级 1 HTML

    &lt;input
      type=&quot;text&quot;
      placeholder=&quot;输入姓名&quot;
      [(ngModel)]=&quot;name&quot;
      (input)=&quot;oninputSetName($event)&quot;
    /&gt;
    &lt;!-- {{ student?.name ?? &quot;&quot; }}
    {{ name }} --&gt;
    
    &lt;button (click)=&quot;onclickEmitNewName()&quot;&gt;添加&lt;/button&gt;
    &lt;button (click)=&quot;onclickEmitUpdateName()&quot;&gt;更新&lt;/button&gt;

子级 1 TypeScript

    import {
      Component,
      EventEmitter,
      Input,
      OnChanges,
      OnInit,
      Output,
      SimpleChanges,
    } from &#39;@angular/core&#39;;
    
    @Component({
      selector: &#39;app-list-form&#39;,
      templateUrl: &#39;./list-form.component.html&#39;,
      styleUrls: [&#39;./list-form.component.css&#39;],
    })
    export class ListFormComponent implements OnChanges {
      name = &#39;&#39;;
      @Output() newName = new EventEmitter&lt;string&gt;();
      @Input() student: any = null;
      @Output() updatedName = new EventEmitter&lt;any&gt;();
      oninputSetName(event: any) {
        this.name = event.target.value;
      }
    
      ngOnChanges(changes: SimpleChanges): void {
        console.log(&#39;list-form: changes happen &#39;, changes);
        this.name = changes[&#39;student&#39;]?.currentValue?.name ?? &#39;&#39;;
      }
    
      change(event: any) {
        this.name = event.target.value;
      }
    
      onclickEmitNewName() {
        this.newName.emit(this.name);
        this.name = &#39;&#39;;
      }
      onclickEmitUpdateName() {
        // if (this.name == &#39;&#39;) return;
        if (!this.name) return;
        this.updatedName.emit({
          name: this.name,
          index: this.student.index,
        });
      }
    }

子级 2 HTML

    &lt;li&gt;
      {{ name }} -- {{ index }}
      &lt;button (click)=&quot;onclickDeleteName()&quot;&gt;删除&lt;/button&gt;
      &lt;button (click)=&quot;onclickEditName()&quot;&gt;编辑&lt;/button&gt;
    &lt;/li&gt;

子级 2 TypeScript

    import { Component, EventEmitter, Input, Output } from &#39;@angular/core&#39;;
    
    @Component({
      selector: &#39;app-list-item&#39;,
      templateUrl: &#39;./list-item.component.html&#39;,
      styleUrls: [&#39;./list-item.component.css&#39;],
    })
    export class ListItemComponent {
      @Input() name = &#39;&#39;;
      @Input() index = 0;
    
      @Output() deleteNameByIndex = new EventEmitter&lt;number&gt;();
      @Output() editNameById = new EventEmitter&lt;any&gt;();
      onclickDeleteName() {
        this.deleteNameByIndex.emit(this.index);
      }
      onclickEditName() {
        let obj = {
          index: this.index,
          name: this.name,
        };
        this.editNameById.emit(obj);
      }
    }
英文:

Parent Html

&lt;section&gt;
&lt;!-- input field where i enter my data --&gt;
&lt;app-list-form
(newName)=&quot;onemitAddNewName($event)&quot;
[student]=&quot;student&quot;
(updatedName)=&quot;updateThisNameInList($event)&quot;
&gt;&lt;/app-list-form&gt;
&lt;!-- rendering my list --&gt;
&lt;ul&gt;
&lt;app-list-item
*ngFor=&quot;let studentName of students; let i = index&quot;
[name]=&quot;studentName&quot;
[index]=&quot;i&quot;
(deleteNameByIndex)=&quot;onemitDeleteNameByIndex($event)&quot;
(editNameById)=&quot;onemitEditNameById($event)&quot;
[
&gt;&lt;/app-list-item&gt;
&lt;/ul&gt;
&lt;/section&gt;

Parent ts

import { Component } from &#39;@angular/core&#39;;
@Component({
selector: &#39;app-root&#39;,
templateUrl: &#39;./app.component.html&#39;,
styleUrls: [&#39;./app.component.css&#39;],
})
export class AppComponent {
students = [&#39;huzaifa&#39;, &#39;hunzila&#39;, &#39;waqar&#39;, &#39;sibte&#39;, &#39;shahzeen&#39;];
student = null;
onemitAddNewName(newName: string) {
this.students.push(newName);
}
onemitDeleteNameByIndex(index: number) {
this.students.splice(index, 1);
}
onemitEditNameById(student: any) {
this.student = student;
}
updateThisNameInList(student: any) {
let newName = student.name;
let index = student.index;
this.students.splice(index, 1, newName);
}
}

child 1 html

&lt;input
type=&quot;text&quot;
placeholder=&quot;enter name&quot;
[(ngModel)]=&quot;name&quot;
(input)=&quot;oninputSetName($event)&quot;
/&gt;
&lt;!-- {{ student?.name ?? &quot;&quot; }}
{{ name }} --&gt;
&lt;button (click)=&quot;onclickEmitNewName()&quot;&gt;Add&lt;/button&gt;
&lt;button (click)=&quot;onclickEmitUpdateName()&quot;&gt;Update&lt;/button&gt;

child 1 ts

import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
} from &#39;@angular/core&#39;;
@Component({
selector: &#39;app-list-form&#39;,
templateUrl: &#39;./list-form.component.html&#39;,
styleUrls: [&#39;./list-form.component.css&#39;],
})
export class ListFormComponent implements OnChanges {
name = &#39;&#39;;
@Output() newName = new EventEmitter&lt;string&gt;();
@Input() student: any = null;
@Output() updatedName = new EventEmitter&lt;any&gt;();
oninputSetName(event: any) {
this.name = event.target.value;
}
ngOnChanges(changes: SimpleChanges): void {
console.log(&#39;list-form: changes happen &#39;, changes);
this.name = changes[&#39;student&#39;]?.currentValue?.name ?? &#39;&#39;;
}
change(event: any) {
this.name = event.target.value;
}
onclickEmitNewName() {
this.newName.emit(this.name);
this.name = &#39;&#39;;
}
onclickEmitUpdateName() {
// if (this.name == &#39;&#39;) return;
if (!this.name) return;
this.updatedName.emit({
name: this.name,
index: this.student.index,
});
}
}

child 2 html

&lt;li&gt;
{{ name }} -- {{ index }}
&lt;button (click)=&quot;onclickDeleteName()&quot;&gt;delete&lt;/button&gt;
&lt;button (click)=&quot;onclickEditName()&quot;&gt;edit&lt;/button&gt;
&lt;/li&gt;

child 2 ts

import { Component, EventEmitter, Input, Output } from &#39;@angular/core&#39;;
@Component({
selector: &#39;app-list-item&#39;,
templateUrl: &#39;./list-item.component.html&#39;,
styleUrls: [&#39;./list-item.component.css&#39;],
})
export class ListItemComponent {
@Input() name = &#39;&#39;;
@Input() index = 0;
@Output() deleteNameByIndex = new EventEmitter&lt;number&gt;();
@Output() editNameById = new EventEmitter&lt;any&gt;();
onclickDeleteName() {
this.deleteNameByIndex.emit(this.index);
}
onclickEditName() {
let obj = {
index: this.index,
name: this.name,
};
this.editNameById.emit(obj);
}
}

huangapple
  • 本文由 发表于 2023年2月6日 14:40:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75358077.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定