javascriptangularjshtml

How to target a html element with a controller in Angular?


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();
        }
    };
}]);

Solution

  • 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).