programing

Angular와 유사한 @Input으로 자 컴포넌트에 대한 Angular pass 콜백 함수JS 방식

fastcode 2023. 3. 21. 22:38
반응형

Angular와 유사한 @Input으로 자 컴포넌트에 대한 Angular pass 콜백 함수JS 방식

Angular JS에는 명령어에 콜백을 전달할 수 있는& 파라미터가 있습니다(Angular ).JS 콜백 방식Callback을 Callback으로 전달할 수 있습니까?@Input각도 구성 요소(아래와 같은 것)의 경우만약 아니라면 앵귤러와 가장 가까운 것이JS가?

@Component({
    selector: 'suggestion-menu',
    providers: [SuggestService],
    template: `
    <div (mousedown)="suggestionWasClicked(suggestion)">
    </div>`,
    changeDetection: ChangeDetectionStrategy.Default
})
export class SuggestionMenuComponent {
    @Input() callback: Function;

    suggestionWasClicked(clickedEntry: SomeModel): void {
        this.callback(clickedEntry, this.query);
    }
}


<suggestion-menu callback="insertSuggestion">
</suggestion-menu>

나는 그것이 나쁜 해결책이라고 생각한다.다음 컴포넌트에 함수를 전달하려면@Input(),@Output()데코레이터는 당신이 찾고 있는 것입니다.

export class SuggestionMenuComponent {
    @Output() onSuggest: EventEmitter<any> = new EventEmitter();

    suggestionWasClicked(clickedEntry: SomeModel): void {
        this.onSuggest.emit([clickedEntry, this.query]);
    }
}

<suggestion-menu (onSuggest)="insertSuggestion($event[0],$event[1])">
</suggestion-menu>

갱신하다

이 답변은 Angular 2가 아직 알파 위치에 있고 많은 기능을 사용할 수 없거나 문서화되지 않은 상태에서 제출되었습니다.다음 방법은 여전히 유효하지만, 이 방법은 완전히 구식입니다.아래의 답변보다 수락된 답변을 적극 추천합니다.

원답

네, 실제로는 그렇습니다만, 스코프가 올바르게 설정되어 있는 것을 확인해 주세요.이걸 위해 난 재산을 이용해서this내가 원하는 걸 의미해

@Component({
  ...
  template: '<child [myCallback]="theBoundCallback"></child>',
  directives: [ChildComponent]
})
export class ParentComponent{
  public theBoundCallback: Function;

  public ngOnInit(){
    this.theBoundCallback = this.theCallback.bind(this);
  }

  public theCallback(){
    ...
  }
}

@Component({...})
export class ChildComponent{
  //This will be bound to the ParentComponent.theCallback
  @Input()
  public myCallback: Function; 
  ...
}

경우에 따라서는 상위 구성 요소에서 비즈니스 논리를 수행해야 할 수 있습니다.다음 예에서는 부모 컴포넌트가 제공하는 논리에 따라 테이블 행을 렌더링하는 자식 컴포넌트가 있습니다.

@Component({
  ...
  template: '<table-component [getRowColor]="getColor"></table-component>',
  directives: [TableComponent]
})
export class ParentComponent {

 // Pay attention on the way this function is declared. Using fat arrow (=>) declaration 
 // we can 'fixate' the context of `getColor` function
 // so that it is bound to ParentComponent as if .bind(this) was used.
 getColor = (row: Row) => {
    return this.fancyColorService.getUserFavoriteColor(row);
 }

}

@Component({...})
export class TableComponent{
  // This will be bound to the ParentComponent.getColor. 
  // I found this way of declaration a bit safer and convenient than just raw Function declaration
  @Input('getRowColor') getRowColor: (row: Row) => Color;

  renderRow(){
    ....
    // Notice that `getRowColor` function holds parent's context because of a fat arrow function used in the parent
    const color = this.getRowColor(row);
    renderRow(row, color);
  }
}

여기서 두 가지를 시연해 보겠습니다.

  1. .fat 화살표(=>)는 올바른 컨텍스트를 유지하기 위해 .fat(이것) 대신 기능합니다.
  2. 자 컴포넌트의 콜백함수를 타이프 세이프 선언합니다.

Snare Chops가 제시한 답변에 대한 대안입니다.

템플릿에서 .bind(이것)를 사용하면 동일한 효과를 얻을 수 있습니다.깔끔하지는 않지만 몇 줄의 회선을 절약할 수 있습니다.저는 지금 앵글 2.4.0을 하고 있어요.

@Component({
  ...
  template: '<child [myCallback]="theCallback.bind(this)"></child>',
  directives: [ChildComponent]
})
export class ParentComponent {

  public theCallback(){
    ...
  }
}

@Component({...})
export class ChildComponent{
  //This will be bound to the ParentComponent.theCallback
  @Input()
  public myCallback: Function; 
  ...
}

맥스 팔의 대답에 대한 대안.

콜백 함수를 부모 컴포넌트의 화살표 함수로 정의하여 바인드할 필요가 없습니다.

@Component({
  ...
  // unlike this, template: '<child [myCallback]="theCallback.bind(this)"></child>',
  template: '<child [myCallback]="theCallback"></child>',
  directives: [ChildComponent]
})
export class ParentComponent {

   // unlike this, public theCallback(){
   public theCallback = () => {
    ...
  }
}

@Component({...})
export class ChildComponent{
  //This will be bound to the ParentComponent.theCallback
  @Input()
  public myCallback: Function; 
  ...
}

예를 들어 로그인 모달 창을 사용하고 있습니다.모달 창은 부모, 로그인 폼은 자녀, 로그인 버튼은 부모 닫기 함수로 호출합니다.

부모 모달에는 모달 닫는 기능이 포함되어 있습니다.이 부모는 닫기 함수를 로그인 자식 구성 요소에 전달합니다.

import { Component} from '@angular/core';
import { LoginFormComponent } from './login-form.component'

@Component({
  selector: 'my-modal',
  template: `<modal #modal>
      <login-form (onClose)="onClose($event)" ></login-form>
    </modal>`
})
export class ParentModalComponent {
  modal: {...};

  onClose() {
    this.modal.close();
  }
}

자녀 로그인 컴포넌트가 로그인 폼을 송신한 후 부모의 콜백 기능을 사용하여 부모 모드를 닫습니다.

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

@Component({
  selector: 'login-form',
  template: `<form (ngSubmit)="onSubmit()" #loginForm="ngForm">
      <button type="submit">Submit</button>
    </form>`
})
export class ChildLoginComponent {
  @Output() onClose = new EventEmitter();
  submitted = false;

  onSubmit() {
    this.onClose.emit();
    this.submitted = true;
  }
}

인수가 있는 메서드 전달, 템플릿 내 .bind 사용

@Component({
  ...
  template: '<child [action]="foo.bind(this, 'someArgument')"></child>',
  ...
})
export class ParentComponent {
  public foo(someParameter: string){
    ...
  }
}

@Component({...})
export class ChildComponent{

  @Input()
  public action: Function; 

  ...
}

Angular 13(2022년 3월 기준)은 다음과 같습니다.

추신: 이것은 다른 사람들이 대답한 것과 거의 비슷합니다.Angular 13에서 작동한다는 것을 알리기 위해 이 답변을 추가하는 것입니다.

부모 컴포넌트에서 함수를 일반 함수가 아닌 플랫 애로우로 정의합니다.

callBackFn= (args: string): void => {
  // callback code here
  // This will work (Flat Arrow)
}
// callbackFn(args: string): void {
//   //This type of definition will not work.
// }

콜백 함수를 자 컴포넌트에 속성으로 전달합니다.

<app-child [callBack]=”callBackFn”></app-child>

자 구성 요소에서 입력으로 콜백 함수를 수신합니다.정의는 부모에 정의된 정의와 일치해야 합니다.

@Input() callBack: (args: string) => void;

그런 다음 하위 구성 요소에서 이 함수를 호출합니다.이것은 하위 구성 요소 템플릿이라고도 할 수 있습니다.

this.callBack('Test');

또는

<button (click)="callBack('Test')"></button>

그러나 이 방법이 좋은지 아닌지 확실하지 않다. React JS에서도 비슷한 어프로치를 볼 수 있습니다.이 어프로치는 훌륭합니다만, 각도에서 어떻게 동작하는지는 아직 확실하지 않습니다.또, 그 영향은 어떨지 알 수 없습니다.

이 어프로치에 대한 코멘트를 주시면 감사하겠습니다.

또 다른 대안.

OP op op op op op op op op op op 。 구체적으로 를 언급하고 이 함수는 가 제안하는이는 @serginho가 제안하는 것으로, @serginho가 제안하는 것으로 간주됩니다.서긴호@Output ★★★★★★★★★★★★★★★★★」EventEmitter.

단, 콜백과 이벤트 사이에는 다음과 같은 차이가 있습니다.콜백을 사용하면 하위 구성 요소가 부모로부터 일부 피드백 또는 정보를 가져올 수 있지만 이벤트는 피드백 없이 어떤 일이 발생했음을 알릴 수 있을 뿐입니다.

색상이나 컴포넌트가 처리해야 할 요소 목록을 얻는 등 피드백이 필요한 사용 사례가 있습니다.몇 가지 답변에 따라 바운드 함수를 사용하거나 인터페이스를 사용할 수 있습니다(이것은 항상 제가 선호하는 사항입니다).

이러한 필드가 있는 모든 데이터베이스 테이블에서 사용할 요소 {id, name} 목록에서 작동하는 일반 구성 요소가 있다고 가정합니다.이 컴포넌트는 다음과 같습니다.

  • 일련의 요소(페이지)를 검색하여 목록으로 표시하다
  • 요소를 제거하다
  • 요소가 클릭되었음을 알립니다.그러면 부모가 어떤 액션을 취할 수 있습니다.
  • 요소의 다음 페이지를 검색할 수 있습니다.

자 컴포넌트

을 사용할 1개의 이 필요합니다.@Input() 3/3@Output()파라미터(그러나 부모로부터의 피드백은 없습니다). ㅇㅇ.<list-ctrl [items]="list" (itemClicked)="click($event)" (itemRemoved)="removeItem($event)" (loadNextPage)="load($event)" ...>@Input():

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

export interface IdName{
  id: number;
  name: string;
}

export interface IListComponentCallback<T extends IdName> {
    getList(page: number, limit: number): Promise< T[] >;
    removeItem(item: T): Promise<boolean>;
    click(item: T): void;
}

@Component({
    selector: 'list-ctrl',
    template: `
      <button class="item" (click)="loadMore()">Load page {{page+1}}</button>
      <div class="item" *ngFor="let item of list">
          <button (click)="onDel(item)">DEL</button>
          <div (click)="onClick(item)">
            Id: {{item.id}}, Name: "{{item.name}}"
          </div>
      </div>
    `,
    styles: [`
      .item{ margin: -1px .25rem 0; border: 1px solid #888; padding: .5rem; width: 100%; cursor:pointer; }
      .item > button{ float: right; }
      button.item{margin:.25rem;}
    `]
})
export class ListComponent implements OnInit {
    @Input() callback: IListComponentCallback<IdName>; // <-- CALLBACK
    list: IdName[];
    page = -1; 
    limit = 10;

    async ngOnInit() {
      this.loadMore();
    }
    onClick(item: IdName) {
      this.callback.click(item);   
    }
    async onDel(item: IdName){ 
        if(await this.callback.removeItem(item)) {
          const i = this.list.findIndex(i=>i.id == item.id);
          this.list.splice(i, 1);
        }
    }
    async loadMore(){
      this.page++;
      this.list = await this.callback.getList(this.page, this.limit); 
    }
}

상위 컴포넌트

이제 부모에서 목록 구성 요소를 사용할 수 있습니다.

import { Component } from "@angular/core";
import { SuggestionService } from "./suggestion.service";
import { IdName, IListComponentCallback } from "./list.component";

type Suggestion = IdName;

@Component({
  selector: "my-app",
  template: `
    <list-ctrl class="left" [callback]="this"></list-ctrl>
    <div class="right" *ngIf="msg">{{ msg }}<br/><pre>{{item|json}}</pre></div>
  `,
  styles:[`
    .left{ width: 50%; }
    .left,.right{ color: blue; display: inline-block; vertical-align: top}
    .right{max-width:50%;overflow-x:scroll;padding-left:1rem}
  `]
})
export class ParentComponent implements IListComponentCallback<Suggestion> {
  msg: string;
  item: Suggestion;

  constructor(private suggApi: SuggestionService) {}

  getList(page: number, limit: number): Promise<Suggestion[]> {
    return this.suggApi.getSuggestions(page, limit);
  }
  removeItem(item: Suggestion): Promise<boolean> {
    return this.suggApi.removeSuggestion(item.id)
      .then(() => {
        this.showMessage('removed', item);
        return true;
      })
      .catch(() => false);
  }
  click(item: Suggestion): void {
    this.showMessage('clicked', item);
  }
  private showMessage(msg: string, item: Suggestion) {
    this.item = item;
    this.msg = 'last ' + msg;
  }
}

에 주의:<list-ctrl>this(부모 컴포넌트)를 콜백오브젝트로 지정합니다.또 하나의 장점은 부모 인스턴스를 전송할 필요가 없으며, 사용 사례에서 허용하는 경우 인터페이스를 구현하는 서비스 또는 모든 개체가 될 수 있다는 것입니다.

완전한 예는 이 스택블리츠에 있습니다.

관측 가능한 패턴을 사용합니다.Observable 값(Subject가 아님)을 Input 매개 변수에 넣고 상위 구성 요소에서 관리할 수 있습니다.콜백 기능은 필요 없습니다.

예:https://stackoverflow.com/a/49662611/4604351 를 참조해 주세요.

.bind(this)메서드를 실행하려면 메서드 이름 뒤에 괄호 쌍을 넣어야 했습니다.

Parent 컴포넌트:

.ts 파일에서:

    this.pillTabs = [
        { tabName: 'Subscribers', tabMethod: this.showSubscribers.bind(this) },
        { tabName: 'Exemplars', tabMethod: this.showExemplars.bind(this) }
    ];

.html 파일에서:

    <pill-tabs [pillTabs]="pillTabs"></pill-tabs>

Child 구성 요소:

.ts 파일에서:

    @Input() pillTabs: PillTab[];

.html 파일에서:

    <div *ngFor="let pillTab of pillTabs; let i = index">
        <input type="radio" id="{{'radio-' + i}}" name="tabs" [checked]="pillTab.checked"
            (click)="pillTab.tabMethod()" />
        <label class="tab" for="{{'radio-' + i}}">{{pillTab.tabName}}</label>
    </div>

메서드 뒤에 괄호 쌍이 없을 때는 코드가 작동하지 않았습니다.

      (click)="pillTab.tabMethod"

그리고 괄호 한 쌍을 넣자 코드가 작동하기 시작했습니다.

      (click)="pillTab.tabMethod()"

누군가 도움이 되길 바랍니다.

현재의 대답은...로 요약할 수 있다.

@Component({
  ...
  template: '<child [myCallback]="theCallback"></child>',
  directives: [ChildComponent]
})
export class ParentComponent{
  public theCallback(){
    ...
  }
}

@Component({...})
export class ChildComponent{
  //This will be bound to the ParentComponent.theCallback
  @Input()
  public myCallback: Function; 
  ...
}

언급URL : https://stackoverflow.com/questions/35328652/angular-pass-callback-function-to-child-component-as-input-similar-to-angularjs

반응형