0

I want to use the HTML5 audio element attributes (play/stop/pause/...) in a controller in Angular. But I have multiple audio elements generated by a foreach loop so selecting by ID is not an option (except if I give id's based on the index of my foreach, which doesn't seem the right way).

My abbreviated html:

<div ng-controller="LoopController">
    <a ng-click="playLoop()">Play</a>
    <audio id="music" controls loop>
        <source src="songs/music1.mp3" type="audio/mpeg">
    </audio>
</div>

<div ng-controller="LoopController">
    <a ng-click="playLoop()">Play</a>
    <audio id="music" controls loop>
        <source src="songs/music2.mp3" type="audio/mpeg">
    </audio>
</div>

Angular controller:

gloopsApp.controller('LoopController', ['$scope', function($scope) {
    var music = document.getElementById('music');
    //here I want to target the audio element that is in this controller

    $scope.playLoop = function() {
        if (music.paused) {
            music.play();
        } else {
            music.pause();
        }
    };
}]);
3
  • I suggest adding the foreach loop in the example code or adding a plunker to demonstrate. It's difficult to see what's wrong. Commented Mar 26, 2016 at 22:07
  • There are 2 elements with the ID music now , I want to target the audio element that is currently encapsulated in that controller so that it only targets 1 audio element Commented Mar 26, 2016 at 22:09
  • 1
    I added a solution that is the more Angular way of doing this, that will help with your encapsulation issue. Commented Mar 26, 2016 at 22:22

2 Answers 2

2

You can get the clicked element using $event and find the next element.

Also, you can't have more than one dom element with the same id, so use class instead.

The following was not tested yet but should work:

        <div ng-controller="LoopController">
        <a ng-click="playLoop(event)">
            Play
        </a>
        <audio class="music" controls loop>
          <source src="songs/music1.mp3" type="audio/mpeg">
        </audio>
    </div>

    <div ng-controller="LoopController">
        <a ng-click="playLoop($event)">
            Play
        </a>
        <audio class="music" controls loop>
          <source src="songs/music2.mp3" type="audio/mpeg">
        </audio>
    </div>

Your js:

gloopsApp.controller('LoopController', ['$scope', function($scope) {

  //here I want to target the audio element that is in this controller

  $scope.playLoop = function($event) {

    var music = angular.element($event.target).next('.music');//or $event.currentTarget

    if (music.paused) {
        music.play();
    } 
    else { 
        music.pause();
    }

   //consider refactoring to 
   //music.paused ? music.play() : music.pause();
  };
}]);

In addition, I don't really see a reason why you want each div to have its own controller (although technically it's ok to do so), simply assigning the controller to the parent element should work just fine and will be better performance assuming you have many music elements (just 1 scope instance instead of N instances).

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

4 Comments

This still gives me an 'undefined' for the music variable
Have you added the $event to the html? try using $event.currentTarget instead of $event.target and see if it works. If not - run console.log($event) and see what it gives you
With the edit I made in your answer it worked, is this a good way to do this?
It is definitely a valid way to do it without refactoring your whole code. However, you should look into creating a directive as @Dr Jones suggested as well as this is a more 'Angular way' of doing it.
1

So a more Angular way of doing this code is to write a Directive. This helps with DRY and encapsulating functionality. The pseudo Angular code of this would be:

Controller HTML - This replaces your existing repeated code. - controller.js

<song-player src="songs/music1.mp3"></song-player>
<song-player src="songs/music2.mp3"></song-player>

Song-player Directive HTML - e.g. song-player.html

<div class="songPlayerContainer">
    <a ng-click="playLoop()">Play</a>
    <audio id="music" controls loop>
        <source src="{{src}}" type="audio/mpeg">
    </audio>
</div>

song-player Directive Javascript - e.g. song-player.js

gloopsApp.directive('songPlayer', function() {
    return {
      restrict: 'E',
      template: '<path to your template>/song-player.html
      scope: { 'src': '=' },
      link: function(scope, element, attrs) {
        var music = $(element).find('music');
         scope.playLoop = function() {
           if (music.paused) {
             music.play();
         } else {
            music.pause();
         }
    };
});

2 Comments

But the playLoop function is executed with an ng-click on a button outside of this directive? Sorry I'm still in the learning progress of Angular
Actually it's within this directive... I forgot to add the directive HTML - Let me clarify 2 seconds.

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.