Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS multiple expressions concatenating in interpolation with a URL

I know this is long, but please bear with me. The problem is easy to understand, just takes some writing to fully explain it.

Right now I'm getting this error

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

I've done all the reading in the documentation, but I still can't find a workaround for my problem.

I'm using $http.get on a private online source that has data that is similar to the form of a json file (so I can't modify the data). The data looks like this:

... "items": [   {    "kind": "youtube#searchResult",    "etag": "\"N5Eg36Gl054SUNiWWc-Su3t5O-k/A7os41NAa_66TUu-1I-VxH70Rp0\"",    "id": {       "kind": "youtube#video",       "videoID": "MEoSax3BEms"       },    },    {     "kind": "youtube#searchResult",     "etag": "\"N5Eg36Gl054SUNiWWc-Su3t5O-k/VsH9AmnQecyYBLJrl1g3dhewrQo\"",     "id": {        "kind": "youtube#video",        "videoID": "oUBqFlRjVXU"        },     }, ... 

I'm trying to interpolate the videoId of each item into my HTML iframe that embeds the YouTube video. In my controller.js file, I'm setting the promise object after the $http.get as such

$http.get('privatesource').success(function(data) {   $scope.videoList = data.items; }); 

So now the variable "$scope.videoList" is mapped to data.items, which has a lot of video elements. In my HTML file, I can retrieve the videoID of each video by using

<ul class="videos">   <li ng-repeat="video in videoList">     <span>{{video.id.videoID}}</span>   </li> </ul> 

and this lists all the videoID's. But if I try to concatenate these values to a URL, like https://youtube.com/embed/, it does not work.

<div ng-repeat="video in videoList">     <iframe id="ytplayer" type="text/html" width="640" height="360"       ng-src="https://www.youtube.com/embed/{{video.id.videoId}}"       frameborder="0" allowfullscreen></iframe> </div> 

Is there a way that I can get the videoID to be interpolated into the youtube URL? I've tried whitelisting by using the $sceDelegateProvider as follows but it still does not work

$sceDelegateProvider.resourceUrlWhitelist([   'self',   'https://www.youtube.com/**']); 

Any help is appreciated. Thanks!

like image 729
user2669464 Avatar asked May 01 '14 09:05

user2669464


2 Answers

An alternative to @tasseKATT's answer (which doesn't require a controller function) is to use string concatenation directly in the expression a filter:

angular.module('myApp')   .filter('youtubeEmbedUrl', function ($sce) {     return function(videoId) {       return $sce.trustAsResourceUrl('http://www.youtube.com/embed/' + videoId);     };   }); 
<div ng-src="{{ video.id.videoId | youtubeEmbedUrl }}"></div> 

I've found this particularly useful when using SVG icon sprites, which requires you to use the xlink:href attribute on the SVG use tag - this is subject to the same SCE rules. Repeating the controller function everywhere I needed to use a sprite seemed silly so I used the filter method instead.

angular.module('myApp')   .filter('svgIconCardHref', function ($sce) {     return function(iconCardId) {       return $sce.trustAsResourceUrl('#s-icon-card-' + iconCardId);     };   }); 
<svg><use xlink:href="{{ type.key | svgIconCardHref }}"></use></svg> 

Note: I had previously tried just simple string concatenation inside the expression. However this led to some unexpected behaviour where the browser would interpret the expression before Angular had a chance to parse it and replace with the real href. Since there is no ng-src equivalent for the use tag's xlink:href I opted for the filter, which seems to have solved the issue.

like image 110
Tom Spencer Avatar answered Oct 05 '22 21:10

Tom Spencer


Since 1.2 you can only bind one expression to *[src], *[ng-src] or action. You can read more about it here.

Try this instead:

In Controller:

$scope.getIframeSrc = function (videoId) {   return 'https://www.youtube.com/embed/' + videoId; }; 

HTML:

ng-src="{{getIframeSrc(video.id.videoId)}}" 

Note that you still need to whitelist it as you have, or you will get locked loading resource from url not allowed by $sceDelegate policy.

like image 24
tasseKATT Avatar answered Oct 05 '22 19:10

tasseKATT