1

I'm working with a parent component that references a child component that plays an audio file in Angular 2. Initially, I'm passing the audio file an input variable of _audioState which contains a string value of "Listen". When the audio button is clicked, this value changes to "Playing" and then "Replay" once the audio file is done playing. These string value changes happen in the audio child component.

When the button with the nextAudio function is clicked in the parent component, I want to reassign _audioState back to "Listen", but the input binding isn't working in the parent once the child component changes this value.

I'm still learning Angular 2 and wasn't sure the best way to get this working. I appreciate any suggestions. My code is below.

Parent Component:

 @Component({
    selector: 'parent-component',
    template: ' <div>
                    <button (click)="nextAudio()"></button>
                    <audio-button [audioPath]="_audioPath"  
                    [audioSrc]="_audioCounter" [audioState]="_audioState">          
                    </audio-button>
                </div>',
    styleUrls: ['./parent-component.less']
})

export class ParentComponent {
 _audioPath: string = "../audio/";
 _audioCounter: number = 1;
 _audioState: string = "Listen";

  nextAudio(): void{
    this._audioCounter = this._audioCounter + 1;
    this._audioState = "Listen";
  }  
}

Child Component:

@Component({
    selector: 'audio-button',
    template: '<button (click)="playSound()"><i class="fa fa-volume-up"></i>    
    {{audioState}}</button>',
    styleUrls: ['./audio-button.component.less']
})
export class AudioButtonComponent {
    @Input() audioPath: string;
    @Input() audioSrc: string;
    @Input() audioState: string;

playSound(): void {
    let sound: any = new Audio(this.audioPath + this.audioSrc + ".mp3");
    sound.play();
    this.audioState = "Playing";
    sound.addEventListener('ended', () => {
        this.audioState = "Replay";
    }, false)

    event.preventDefault();
   }
}

1 Answer 1

1

there are several ways to do it, including of

1. (Recommand) Using ngrx/store

References

Adding Redux with NgRx/Store to Angular (2+) – Part 1 (Oren Farhi)

2. EventEmitter

For example,

@Component({
selector: 'parent-component',
template: `<div>
                Current state : {{_audioState}}
                <hr />
                <audio-button [audioPath]="_audioPath"
                [audioSrc]="_audioCounter" [audioState]="_audioState"
                (emit-status)="updateStatus($event)"
                >
                </audio-button>
            </div>`
})

export class ParentComponent {
  _audioPath: string = "../audio/";
  _audioCounter: number = 1;
  _audioState: string = "Listen";

  nextAudio(): void{ 
    this._audioCounter = this._audioCounter + 1;
    this._audioState = "Listen";
  }

  private updateStatus(status:string){
    this._audioState= status;
  }
}

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

@Component({
   selector: 'audio-button',
   template: `<button (click)="playSound()"><i class="fa fa-volume-up"></i>
{{audioState}}</button>`,
})

export class AudioButtonComponent {
  @Input() audioPath: string;
  @Input() audioSrc: string;
  @Input() audioState: string;
  @Output('emit-status') emitStatus = new EventEmitter<any>();

  
  playSound(): void {
    //...

    //emit data to parent
    this.emitStatus.emit("playing");
  }
}

3.ViewChild

@Component({
   selector: 'parent-component',
   template: `<div>
            Current state : {{audioBtn.audioState}}
            <hr />
            <audio-button [audioPath]="_audioPath"
            [audioSrc]="_audioCounter" [audioState]="_audioState"
            (emit-status)="updateStatus($event)"
            >
            </audio-button>
        </div>`
})

export class ParentComponent {
    @ViewChild(AudioButtonComponent) audioBtn: AudioButtonComponent;
}

export class AudioButtonComponent {
   //...
  @Input() audioState: string;
  playSound(): void {
     //...
     this.audioState = "playing";
  }
}

Result

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! Appreciate the help. I've used output and EventEmitter before, but wasn't sure if there was a better way to do it.
I think one of the cleanest way on state management is using ngrx/stroe. However in a not-so-complex application, we can use EventEmitter or ViewChild to do it.
Sounds good. I was looking into using ViewChild but couldn't reference the child component properly. ViewChild can only be used in ngAfterViewInit() right?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.