Como emitir um evento de pai para filho?

111

No momento, estou usando o Angular 2. Normalmente usamos @Output() addTab = new EventEmitter<any>();e addTab.emit()para emitir um evento para o componente pai.

Existe alguma maneira de fazermos vice-cersa, de pai para filho?

code1
fonte
2
Basta alterar o valor de uma expressão passada como entrada para o componente filho, e o componente filho o receberá (e será informado por ngOnChanges). Você também pode emitir um evento usando um serviço compartilhado que tem um Observable.
JB Nizet

Respostas:

192

Usando RxJs , você pode declarar um Subjectem seu componente pai e passá-lo como Observablecomponente filho, o componente filho só precisa se inscrever nele Observable.

Componente-pai

eventsSubject: Subject<void> = new Subject<void>();

emitEventToChild() {
  this.eventsSubject.next();
}

HTML pai

<child [events]="eventsSubject.asObservable()"> </child>    

Componente-filho

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

ngOnInit(){
  this.eventsSubscription = this.events.subscribe(() => doSomething());
}

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}
BlueNC
fonte
13
Além disso, os dados podem ser transmitidos junto com o evento usando essa abordagem. Tipo, this.eventsSubject.next({data});no pai, depois this.events.subscribe(({data}) => doSomething(data));no filho.
vince
2
Eu editei esta ótima resposta para adicionar o cancelamento da inscrição. +1
Umar Farooq Khawaja
1
Provavelmente uma pergunta para iniciantes aqui: por que converter eventsSubjectpara um Observable em vez de apenas inscrevê-lo como um Assunto?
Justin Morgan de
11
Convertendo eventsSubject em um Observable, evita que o componente filho chame next ().
BlueNC
2
Obrigado por esta solução, FUNCIONA, mas estou recebendo um erro no console: "core.js: 6185 ERROR TypeError: Cannot read property 'subscribe' of undefined" O erro está apontando para events.subscribe () no ngOnInit do componente filho: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" Estou usando as versões: "@ angular / cli": "~ 9.1.0" e "rxjs" : "~ 6.5.4"
Eduardo
72

Pelo que eu sei, existem 2 maneiras padrão de fazer isso.

1. @Input

Sempre que os dados no pai são alterados, o filho é notificado sobre isso no método ngOnChanges. A criança pode agir sobre isso. Esta é a maneira padrão de interagir com uma criança.

Parent-Component
public inputToChild: Object;

Parent-HTML
<child [data]="inputToChild"> </child>       

Child-Component: @Input() data;

ngOnChanges(changes: { [property: string]: SimpleChange }){
   // Extract changes to the input property by its name
   let change: SimpleChange = changes['data']; 
// Whenever the data in the parent changes, this method gets triggered. You 
// can act on the changes here. You will have both the previous value and the 
// current value here.
}
  1. Conceito de serviço compartilhado

Criando um serviço e usando um observável no serviço compartilhado. A criança assina e sempre que houver alteração, ela será avisada. Este também é um método popular. Quando você quiser enviar algo diferente dos dados que você passou como entrada, isso pode ser usado.

SharedService
subject: Subject<Object>;

Parent-Component
constructor(sharedService: SharedService)
this.sharedService.subject.next(data);

Child-Component
constructor(sharedService: SharedService)
this.sharedService.subject.subscribe((data)=>{

// Whenever the parent emits using the next method, you can receive the data 
in here and act on it.})
gonsalves prajwal
fonte
Eu tentei o primeiro método e funcionou bem até certo ponto e depois disso eu recebi uma mensagem de erro informando O valor foi alterado agora, antes é falso e agora é verdadeiro. Não me deu nenhuma explicação adicional além desta.
código
2
Considerando que você quer dizer um ExpressionChangedAfterItHasBeenCheckedError, esse erro não será gerado no modo de produção.
user1337
<child [data]="inputToChild"> </child>provavelmente deve ser <child [data]="(inputToChild)"> </child>para obter as alterações
pmc
28

Em um componente pai, você pode usar @ViewChild () para acessar o método / variável do componente filho.

@Component({
  selector: 'app-number-parent',
  templateUrl: './number-parent.component.html'
})
export class NumberParentComponent {
    @ViewChild(NumberComponent)
    private numberComponent: NumberComponent;
    increase() {
       this.numberComponent.increaseByOne();
    }
    decrease() {
       this.numberComponent.decreaseByOne();
    }
} 

Atualizar:

Angular 8 em diante -

@ViewChild(NumberComponent, { static: false })
Hardik Patel
fonte
4
Isso parece mais limpo do que trabalhar com observáveis. A desvantagem é que a criança precisa estar em vista. Se o filho estiver carregado com roteamento, por exemplo, isso falhará como numberComponentestará undefined.
Shy Agam
1
esta é uma boa prática? concordo com o fato de que é mais limpo do que os observáveis, mas tenho dúvidas sobre como manipular as variáveis ​​da criança a partir dos pais. De qualquer forma, funcionou tão bem para minhas necessidades, obrigado!
Takatalvi
1
Essa é uma boa dica. Parece que @ViewChild foi alterado no Angular 8, ou pelo menos tive que especificar as opções de ViewChild. Eu usei isso no meu caso: @ViewChild (Other, {static: false}) private otherComponent: Other;
Rasgou Aurstad
8

Use o decorador @Input () em seu componente filho para permitir que o pai se vincule a esta entrada.

No componente filho, você o declara como está:

@Input() myInputName: myType

Para vincular uma propriedade de pai a filho, você deve adicionar em seu modelo os colchetes de ligação e o nome de sua entrada entre eles.

Exemplo:

<my-child-component [myChildInputName]="myParentVar"></my-child-component>

Mas cuidado, os objetos são passados ​​como referência, portanto, se o objeto for atualizado no filho, o var do pai também será atualizado. Isso pode levar a algum comportamento indesejado em algum momento. Com tipos primários, o valor é copiado.

Para ir mais longe, leia isto:

Documentos: https://angular.io/docs/ts/latest/cookbook/component-communication.html

Noki
fonte
1

No pai, você pode fazer referência ao filho usando @ViewChild. Quando necessário (ou seja, quando o evento seria disparado), você pode apenas executar um método no filho do pai usando a referência @ViewChild.

Paul Evans
fonte