更改HTML5的源“src”属性在AngularJS中没有效果

30

我有一个音频文件列表,以链接的形式呈现,并且有一个 <audio> HTML5 播放器。每个链接调用一个函数,该函数更改 <source> 标记中的 src 以在 <audio> 中播放对应的音频文件:

<audio controls="controls" preload="none">
  <source type="audio/mpeg" src="{{selectedSongPath}}"/>
</audio>

...

<div class="songEntry" ng-repeat="song in songs">
  <a href="" ng-click="songSelect(song.path)">{{song.name}}</a>
</div>

...

$scope.songSelect = function(songPath) {
    $scope.selectedSongPath = songPath;
}

我能看到src在改变,但是没有播放任何内容。路径没问题;如果我使用其中一个路径初始化src,则播放器可以工作。

我做错了什么?


1
你解决了这个问题吗?我也遇到了类似的问题。 - Nick Jennings
12个回答

26

以下是一些angular的方法:

1)使用ng-src替代src=

angular文档解释了为什么这样做: http://docs.angularjs.org/api/ng.directive:ngSrc

在编译阶段,angular会扩展元素以包括正确的src属性。

这将更改HTML5音频元素的src属性,但不幸的是这并不足以播放新歌。您需要通过调用.play()方法来激活音频元素。

糟糕 :(

进一步沿着这条路进行修改建议在控制器内进行DOM操作。这通常意味着这是错误的解决方案。

更好的解决方案是使用服务!!!

2)使用angular服务

// Define a simple audio service 
mpApp.factory('audio',function ($document) {
  var audioElement = $document[0].createElement('audio'); // <-- Magic trick here
  return {
    audioElement: audioElement,

    play: function(filename) {
        audioElement.src = filename;
        audioElement.play();     //  <-- Thats all you need
    }
    // Exersise for the reader - extend this service to include other functions
    // like pausing, etc, etc.

  }
});
在您的控制器中,您可以注入“音频(audio)”,并对其进行任何需要的操作。
例如:
function myAstoundingAudioCtrl($scope, audio) { //<-- NOTE injected audio service

    $scope.songSelect = function(songPath) {
        audio.play(songPath);    //     <---  Thats All You Need !
    }
}

现在你可以将“audio”作为参数添加到任何需要更改音乐的控制器中,并使用此处定义的相同API调用。

作为“service”,整个应用程序只有一个实例。所有对“audio”的调用都指向同一个对象。这对于angular中的所有服务都是真实的。这正是我们在这种情况下所需要的。

该服务在应用程序的根文档上创建一个不可见的HTML5元素,因此无需在视图中的任何位置添加一个<audio>标签。这还有一个额外的好处,即在用户导航到不同的视图时维护歌曲的播放。

请参阅http://docs.angularjs.org/api/ng中的$document服务定义。

希望这能帮到你,伙计 :)


2
这是一个很棒的解决方案。我很感激你在最后解释了它的工作原理!最佳答案。 - David Fariña

21

我也遇到了同样的问题,即使使用了$sce.trustAsResourceUrl后仍然存在这个问题,后来我意识到问题出在HTML上。源应该放在音频标签本身中:

<audio controls data-ng-src="{{yourTrustedUrl}}" ></audio>

2
抱歉跟你说“我也是”,但我真的想感谢你。我花了很多时间卡在这个问题上,已经快疯了。 - Charles Walker
即使这可能有效,但有一个audiovideo元素有多个source子节点的原因:在HTML5中,您可以定义多个文件格式/编解码器,因此浏览器可以选择它最擅长的source。不幸的是,在您的audiovideo元素中定义ngScr时,您会失去这种效益。 - user1438038

5
<audio ng-src="{{getAudioUrl()}}" audioplayer controls></audio>

$scope.getAudioUrl = function() {
return $sce.trustAsResourceUrl('your url');
};

这对我有用,你需要对其进行清理。

4

在AngularJS中,ngSrc不能用于视频,只能用于图片。我的解决方案是:

在视图中:

        <video controls="controls" name="Video Name" ng-src="{{getVideoUrl()}}"></video>

在控制器中:
  $scope.getVideoUrl=function(){
        return $sce.trustAsResourceUrl("media/"+$scope.groupedProjList[$scope.routeId].CONTACTNAME+".mp4");
   };

请将您的网址替换为我的网址:

$sce.trustAsResourceUrl('your Url');

不要忘记在你的模块中添加$sce
angular.module('myApp.controllers', [])
  .controller('MyCtrl1',  function($scope,$http,$routeParams,$sce) {

2

我曾经在使用下拉选择菜单加载视频源时遇到了类似的问题。后来我发现只需要添加$scope.$apply();就可以解决问题了。虽然我还没有时间测试您的代码实现,但我建议您尝试一下这个方法:

$scope.songSelect = function(songPath) {
    $scope.selectedSongPath = songPath;
    $scope.$apply();
}

关于使用$scope.$apply()处理错误存在争议。我认为正确的实现方式如下:

$scope.songSelect = function(songPath) {
    $scope.$apply(function() {
        $scope.selectedSongPath = songPath;
    });
}

据说如果songPath返回未定义,则这种方法可以更好地解决问题;但不幸的是,我找不到我学到这个技巧的链接。如果我找到了,我会将其作为对此答案的评论发布。

希望这有所帮助 :)


1

@SteveOC64的方法涉及创建一个新的Audio实例。如果您已经有了像

<audio id="audio" controls></audio>

您可以尝试以下代码。
    app.factory('audio', function($document) {
    var audioElement = $document[0].getElementById('audio');
    audioElement.autoPlay = true; // as per your requirement

    return {
      audioElement: audioElement,

      play: function(filename) {
        audioElement.src = filename;
        audioElement.play();
      },
      resume: function() {
        audioElement.play();
      },
      pause: function() {
        audioElement.pause();
      },
      stop: function() {
        audioElement.pause();
        audioElement.src = audioElement.currentSrc; /** https://dev59.com/gnPYa4cB1Zd3GeqPeAR-#16978083 **/
      },
      incVol: function() {
        if (audioElement.volume < 1) {
          audioElement.volume = (audioElement.volume + 0.1).toFixed(2);
        }
        return audioElement.volume;
      },
      decVol: function() {
        if (audioElement.volume > 0) {
          audioElement.volume = (audioElement.volume - 0.1).toFixed(2);
        }
        return audioElement.volume;
      },
      timer: function(callback) {
        audioElement.ontimeupdate = function() {
          callback(audioElement.duration, audioElement.currentTime)
        };
      },
    }
  });

在你的控制器中注入audio工厂后,你可以像这样调用它

 audio.play('/api/resource?resource=' + uri);

如果这是事件驱动的,例如在单击另一个元素时播放音频。
$scope.$apply(function() {
   $scope.audioPlayer = true;
   audio.play('/api/resource?resource=' + uri);
   $scope.isAudioPlaying = true;
});

如果有人正在寻找视频工厂: HTML
<video id="video" controls></video>

工厂

app.factory('video', function($document) {
       var videoElement = $document[0].getElementById('video');
       videoElement.autoPlay = true;
       return {
          videoElement: videoElement,
          play: function(filename) {
             videoElement.src = filename;
             videoElement.play();
          },
          resume: function() {
             videoElement.play();
          },
          pause: function() {
             videoElement.pause();
          },
          stop: function() {
             videoElement.pause();
             videoElement.src = videoElement.currentSrc; /** https://dev59.com/gnPYa4cB1Zd3GeqPeAR-#16978083 **/
          },
          incVol: function() {
             if(videoElement.volume < 1) {
                videoElement.volume = (videoElement.volume + 0.1).toFixed(2);
             }
             return videoElement.volume;
          },
          decVol: function() {
             if(videoElement.volume > 0) {
                videoElement.volume = (videoElement.volume - 0.1).toFixed(2);
             }
             return videoElement.volume;
          },
          timer: function(callback) {
             videoElement.ontimeupdate = function() {
                callback(videoElement.duration, videoElement.currentTime)
             };
          },
       }
    });

你可以这样调用它:video.play('/api/resource?resource=' + uri);

希望这能帮到你!


你有没有使用AngularJS处理过onended事件? - Proxy32

1
你也可以使用这个:

<div ng-bind-html-unsafe="audioPlayer"></div>

在你的控制器上:
$scope.audioPlayer="<br /><audio controls><source src=\""+ audiosource + "\" type=\"audio/mpeg\"> Your browser does not support the audio element. </audio>"

注意:

仅在ngBindHtml指令过于受限制且您绝对信任要绑定的内容源时,才应使用此指令。

更多这里


1
我找到的解决方案是在从ajax调用中获取视频URL后立即加载视频。
var video = angular.element('#video_id');
video.load();

0

在更改src后,您还必须加载该文件,因此请尝试在$scope.selectedSongPath = songPath;之后向您的函数songselect添加以下内容:

$scope.load();

我不知道在你的情况下 $scope 是什么,但是 load() 方法应该在你的音频标签对象内部执行。在简单的 JS 中,它看起来像这样:

<audio id="audio-tag" controls="controls" preload="none"> //need the ID to get element
    <source type="audio/mpeg" src="{{selectedSongPath}}"/>
</audio>
....
$scope.selectedSongPath = songPath; //you're changing the src
document.getElementById("audio-tag").load(); //here you load the file (you need equivalent in your framework)

希望能有所帮助。


在我的情况下,ctrl 是整个 <body>。不管怎样,您建议的 $scope.load() 引发了异常:TypeError: Object #<Object> has no method 'load'。@sobol6803 - user1639431
你的简单js解决方案虽然有效,谢谢。有没有类似Angular的解决方案呢?还有,“你需要在你的框架中找到等效方法”是什么意思?@sobol6803 - user1639431
我的意思是,document.get... 只是你的情况下的解决方法。如果你使用 angularjs,你应该在这个框架中找到解决方案。所以在这种情况下,我的答案只是给你一个提示,因为我不知道这个框架,也没有时间去学习它,但是在普通的 js 中,方法肯定是 load,你应该去寻找它。也许,如果你的音频标签像你定义 $scope 一样(通过 ng-something),然后使用 $audio.load() 或类似的东西?这是一个冒险,但现在我不能再帮忙了。抱歉。 - sobol6803
非常感谢你的帮助,我们非常感激,但这只是暂时的解决方法:当我使用 document.get... 时,我需要点击某个链接两次才能加载播放器。@sobol6803 - user1639431
使用load()非常有帮助。非常感谢! - user3941146

0
之前我使用了ngAudio, 但遇到同时播放多个音频的问题。尝试使用HTML5音频标签后,只用一个标签来解决了我的问题。 <audio controls ng-src='{{trusted_url }}'></audio>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接