17

I'm trying to implement a video element in an angular JS app and the ng-src won't read the scope variable

I'm using 1.2.0-rc.2

<!DOCTYPE html>
<html ng-app="ngView">

<head>
   <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>

   <script>
   var app = angular.module('ngView', []);
   function MyControl($scope){
      $scope.file = '1234.mp4';
   }
  </script>
  </head>
  <body ng-controller="MyControl">
      <video controls  ng-src="http://www.thebigdot.com/{{file}}"></video>
  </body>
</html>

If I use a much older version AngularJS lib, it works.

cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.3/angular.min.js (works)

Is this a bug in the latest release or has it been disabled on purpose? What is the work around ?

Ajq
  • 384
  • 2
  • 4
  • 15

5 Answers5

33

Angular 1.2 ships with Strict Contextual Escaping (SCE) enabled by default. You need to tweak your code slightly to make it work.

HTML

Change the markup so that the ng-src binds to a variable and not a URL + variable as you had it setup before:

<video controls ng-src="{{videoUrl}}"></video>

JavaScript

Add $sce to inject the SCE provider and use the $sce.trustAsResourceUrl method to set videoUrl.

function MyControl($scope, $sce) {
    var videoUrl = 'http://www.thebigdot.com/1234.mp4';
    $scope.videoUrl = $sce.trustAsResourceUrl(videoUrl);
}

Here's a JS Bin demo of this setup in action.

Ahmad Mageed
  • 94,561
  • 19
  • 163
  • 174
  • This still looks like a bug to me since they mention that they don't use these for a[href] and img[src] values but it is being used for the video tag. Perhaps video/audio tags need to be considered here as well. – Chris Nicola Oct 15 '13 at 14:57
  • This example nicely explains how to hardcode an absolute path into your controller, but I'm still struggle to figure out how to iterate through a JSON file that's been bound to a $scope; i.e. something more like OP's original example: ng-src="http://www.thebigdot.com/{{file}}" For example, say I have A LOT of videos to dynamically populate inside my template based on an id I am sending to the router via $routeParams Any suggestions would be helpful. – DrewT May 14 '14 at 21:38
  • Oh, actually I was able to solve the issue by referring to the answer here: http://stackoverflow.com/questions/23405162/angularjs-multiple-expressions-concatenating-in-interpolation-with-a-url – DrewT May 14 '14 at 22:26
  • But your demo doesn't work although the video url is fine. – Bagusflyer Jan 08 '16 at 02:13
  • nice i want exactly this but it's for only one video from Json response. in my case i want to display multiple video so please any one can help me? – KAUSHAL J. SATHWARA Jul 22 '16 at 05:14
6

after a bit of debugging I found that the error is this:

Error: [$interpolate:noconcat] Error while interpolating: http://www.thebigdot.com/{{file}} Strict Contextual Escaping disallows interpolations that concatenate multiple expressions when a trusted value is required.  See [http://docs.angularjs.org/api/ng.$sce][1] 

http://errors.angularjs.org/1.2.0-rc.2/$interpolate/noconcat?p0=http%3A%2F%2Fwww.thebigdot.com%2F%7B%7Bfile%7D%7D
    at http://code.angularjs.org/1.2.0-rc.2/angular.js:78:12
    at $interpolate (http://code.angularjs.org/1.2.0-rc.2/angular.js:6953:17)
    at attrInterpolateLinkFn (http://code.angularjs.org/1.2.0-rc.2/angular.js:5367:27)
    at nodeLinkFn (http://code.angularjs.org/1.2.0-rc.2/angular.js:5121:13)
    at compositeLinkFn (http://code.angularjs.org/1.2.0-rc.2/angular.js:4640:15)
    at nodeLinkFn (http://code.angularjs.org/1.2.0-rc.2/angular.js:5115:24)
    at compositeLinkFn (http://code.angularjs.org/1.2.0-rc.2/angular.js:4640:15)
    at compositeLinkFn (http://code.angularjs.org/1.2.0-rc.2/angular.js:4643:13)
    at publicLinkFn (http://code.angularjs.org/1.2.0-rc.2/angular.js:4549:30)
    at http://code.angularjs.org/1.2.0-rc.2/angular.js:1157:27 <video controls="" ng-src="http://www.thebigdot.com/{{file}}"> angular.js:7861

this article explains what is going on and how to disable the Strict Contextual Escaping: http://docs.angularjs.org/api/ng.$sce

user1438038
  • 5,821
  • 6
  • 60
  • 94
akonsu
  • 28,824
  • 33
  • 119
  • 194
  • 1
    I managed to get it working by not concatenating the values, just format the URL in the service rather than the view. – Ajq Oct 15 '13 at 05:18
  • 1
    For new people finding this question, if you're using SCE (enabled by default on modern versions of Angular) you'll need to add the foreign domain to the whitelist. This came up in another question: http://stackoverflow.com/a/21884995/384693 – spikeheap Feb 19 '14 at 16:04
  • Whitelisting is the safest solution. You don't want to use `$sce.trustAsResourceUrl` willy-nilly unless you're absolutely sure you can trust the resulting URL (i.e. that by interpolating a user controlled value you won't enable them to enter a malicious value that could compromise the security of your app). – Ezequiel Muns Mar 03 '16 at 05:54
2

I build this directive

app.directive('loadAudio', function($parse) {
  return {
    restrict: 'EA',
    scope: {
        source: '=',       
    },
    template: '<audio />',
    link: function(scope, iElement, iAttrs) {

      scope.$watch('source', function(value) {
        var audio = iElement.find('audio');

        audio.attr('src',  value);
      }, true);
    }
  }
});

And next is what I write on the template

<load-audio source="your_src" ></load-audio>
Tuan Bach Van
  • 164
  • 2
  • 7
0

I did simple hack before I found previous post. Somebody could found this useful

HTML :

<div id="videoTag"></div>

Controller.js

document.getElementById("videoTag").innerHTML = "<video width='auto' height='auto' controls autoplay src=" + $scope.details.preview + ">Your browser does not support video</video>";
Dmitri Algazin
  • 3,332
  • 27
  • 30
0

I had the same problem with version 1.5.0 of angular. When I changed version (I took the 1.4.8 release) my problem was solved.

.controller('MainCtrl',['$scope','$stateParams','DepartementFactory',
        function($scope,$stateParams,DepartementFactory) {
            $scope.currentDate=new Date();
            DepartementFactory.getDepartements().then(function(data){
               $scope.departements=data;
               $scope.departements.logo="work"; 
            });
        }
    ]
)
<div class="work">
    <a>
        <img src="images/{{departements.logo}}" class="media" alt=""/>
        <div class="caption">
            <div class="work_title">
                <h1>{{departements.libelle}}</h1>
            </div>
        </div>
    </a>
</div>