219

I'm trying to set the src attribute of an iframe from a variable and I can't get it to work...

The markup:

<div class="col-xs-12" ng-controller="AppCtrl">

    <ul class="">
        <li ng-repeat="project in projects">
            <a ng-click="setProject(project.id)" href="">{{project.url}}</a>
        </li>
    </ul>

    <iframe  ng-src="{{trustSrc(currentProject.url)}}">
        Something wrong...
    </iframe>
</div>

controllers/app.js:

function AppCtrl ($scope) {

    $scope.projects = {

        1 : {
            "id" : 1,
            "name" : "Mela Sarkar",
            "url" : "http://blabla.com",
            "description" : "A professional portfolio site for McGill University professor Mela Sarkar."
        },

        2 : {
            "id" : 2,
            "name" : "Good Watching",
            "url" : "http://goodwatching.com",
            "description" : "Weekend experiment to help my mom decide what to watch."    
        }
    };

    $scope.setProject = function (id) {
        $scope.currentProject = $scope.projects[id];
        console.log( $scope.currentProject );

    }
}

With this code, nothing gets inserted into the iframe's src attribute. It's just blank.

Update 1: I injected the $sce dependancy into the AppCtrl and $sce.trustUrl() now works without throwing errors. However it returns TrustedValueHolderType which I'm not sure how to use to insert an actual URL. The same type is returned whether I use $sce.trustUrl() inside the interpolation braces in the attribute src="{{trustUrl(currentProjectUrl))}}" or if I do it inside the controller when setting the value of currentProjectUrl. I even tried it with both.

Update 2: I figured out how to return the url from the trustedUrlHolder using .toString() but when I do that, it throws the security warning when I try to pass it into the src attribute.

Update 3: It works if I use trustAsResourceUrl() in the controller and pass that to a variable used inside the ng-src attribute:

$scope.setProject = function (id) {
    $scope.currentProject = $scope.projects[id];
    $scope.currentProjectUrl = $sce.trustAsResourceUrl($scope.currentProject.url);
    console.log( $scope.currentProject );
    console.log( $scope.currentProjectUrl );

}

My problem seems to be solved by this, although I'm not quite sure why.

isherwood
  • 58,414
  • 16
  • 114
  • 157
emersonthis
  • 32,822
  • 59
  • 210
  • 375

6 Answers6

372

I suspect looking at the excerpt that the function trustSrc from trustSrc(currentProject.url) is not defined in the controller.

You need to inject the $sce service in the controller and trustAsResourceUrl the url there.

In the controller:

function AppCtrl($scope, $sce) {
    // ...
    $scope.setProject = function (id) {
      $scope.currentProject = $scope.projects[id];
      $scope.currentProjectUrl = $sce.trustAsResourceUrl($scope.currentProject.url);
    }
}

In the Template:

<iframe ng-src="{{currentProjectUrl}}"> <!--content--> </iframe>
John Rix
  • 6,271
  • 5
  • 40
  • 46
musically_ut
  • 34,028
  • 8
  • 94
  • 106
  • 1
    I tried it with $sce as you recommended. It causes the error message to go away, but the src attribute of the iframe is still empty. – emersonthis Nov 19 '13 at 11:58
  • 4
    Try using `trustAsResourceUrl`. – musically_ut Nov 19 '13 at 12:12
  • trustAsResourceUrl seems to return the same object as trustAsUrl() – emersonthis Nov 19 '13 at 12:16
  • 9
    ...but this one works when I pass it into the ng-src attribute! Thanks. – emersonthis Nov 19 '13 at 12:23
  • 2
    @Emerson `trustAsResourceUrl` returns a `$sce.RESOURCE_URL` which is needed for `iframe`/`objects` while `trustAsUrl` returns a `$sce.URL` which is a weaker sort of guarantee (and is currently unused as per [the documentation](http://docs.angularjs.org/api/ng.$sce)). – musically_ut Nov 19 '13 at 16:23
  • 1
    ng-src didn't work for me unless I removed the double curly braces( ng-src="currentProjectUrl" ) – baacke Jan 22 '15 at 16:13
  • @baacke I will be surprised if that is the case, [the documentation](https://docs.angularjs.org/api/ng/directive/ngSrc) clearly states that for interpolation, one needs to have `{{}}` in the expression. If you don't need to interpolate anything, then you do not need `ng-src` at all, just the `src` attribute will suffice. – musically_ut Jan 22 '15 at 16:19
  • Annoyingly, you have to include `$sce` in the controller code, and can't include it in services. – RevanProdigalKnight Feb 10 '15 at 14:41
  • $sce.trustAsResourceUrl( works! thanx.. i dnt even knw $sce module is exist and its use.. Angular sucks at this... urrgghh!! i wasted my whole day solving it.. finally i found the ryt solution!! – Rafique Mohammed Nov 01 '15 at 16:13
10

It is the $sce service that blocks URLs with external domains, it is a service that provides Strict Contextual Escaping services to AngularJS, to prevent security vulnerabilities such as XSS, clickjacking, etc. it's enabled by default in Angular 1.2.

You can disable it completely, but it's not recommended

angular.module('myAppWithSceDisabledmyApp', [])
   .config(function($sceProvider) {
       $sceProvider.enabled(false);
   });

for more info https://docs.angularjs.org/api/ng/service/$sce

svarog
  • 9,477
  • 4
  • 61
  • 77
Mohamed Ali
  • 3,717
  • 1
  • 33
  • 39
5

this way i follow and its work for me fine, may it will works for you,

<iframe class="img-responsive" src="{{pdfLoc| trustThisUrl }}" ng-style="{
                height: iframeHeight * 0.75 + 'px'
            }" style="width:100%"></iframe>

here trustThisUrl is just filter,

angular.module("app").filter('trustThisUrl', ["$sce", function ($sce) {
        return function (val) {
            return $sce.trustAsResourceUrl(val);
        };
    }]);
chandrakant
  • 370
  • 3
  • 25
2

Please remove call to trustSrc function and try again like this . {{trustSrc(currentProject.url)}} to {{currentProject.url}}. Check this link http://plnkr.co/edit/caqS1jE9fpmMn5NofUve?p=preview


But according to the Angular Js 1.2 Documentation, you should write a function for getting src url. Have a look on the following code.

Before:

Javascript

scope.baseUrl = 'page';
scope.a = 1;
scope.b = 2;

Html

<!-- Are a and b properly escaped here? Is baseUrl controlled by user? -->
<iframe src="{{baseUrl}}?a={{a}&b={{b}}"

But for security reason they are recommending following method

Javascript

var baseUrl = "page";
scope.getIframeSrc = function() {

  // One should think about their particular case and sanitize accordingly
  var qs = ["a", "b"].map(function(value, name) {
      return encodeURIComponent(name) + "=" +
             encodeURIComponent(value);
    }).join("&");

  // `baseUrl` isn't exposed to a user's control, so we don't have to worry about escaping it.
  return baseUrl + "?" + qs;
};

Html

<iframe src="{{getIframeSrc()}}">
Sajith
  • 2,842
  • 9
  • 37
  • 49
  • The documentation provides this advice in case one is binding to more than one expression in `ng-src` or `src`. Angular 1.2 onwards, one can bind to only one expression in `src` and `ng-src` and the advice is to retrieve an url from the code using a function, if needed. – musically_ut Nov 18 '13 at 13:34
  • But I think there is some mistake in your code. The controller should be like this app.controller('AppCtrl', function($scope) {}); – Sajith Nov 19 '13 at 07:50
  • 1
    Controllers can be [_Globally accessible functions_](http://docs.angularjs.org/api/ng.directive:ngController) as well. – musically_ut Nov 19 '13 at 08:14
  • Ok. Check this link I checked your code with plunker. http://plnkr.co/edit/caqS1jE9fpmMn5NofUve – Sajith Nov 19 '13 at 08:16
  • I have noticed a function "trustSrc" in your code. Please remove that function and try again like this . {{trustSrc(currentProject.url)}} to {{currentProject.url}} – Sajith Nov 19 '13 at 08:48
  • The plunker uses Angular 1.0.8? – musically_ut Nov 19 '13 at 12:13
  • @Sajith please see my updates above. I am able to use trustSrc as you have both suggested, but I can't retrieve the URL from the returned object. I tried calling .toString() on it but it went back to throwing the same error from the beginning. – emersonthis Nov 19 '13 at 12:15
0

select template; iframe controller, ng model update

index.html

angularapp.controller('FieldCtrl', function ($scope, $sce) {
        var iframeclass = '';
        $scope.loadTemplate = function() {
            if ($scope.template.length > 0) {
                // add iframe classs
                iframeclass = $scope.template.split('.')[0];
                iframe.classList.add(iframeclass);
                $scope.activeTemplate = $sce.trustAsResourceUrl($scope.template);
            } else {
                iframe.classList.remove(iframeclass);
            };
        };

    });
    // custom directive
    angularapp.directive('myChange', function() {
        return function(scope, element) {
            element.bind('input', function() {
                // the iframe function
                iframe.contentWindow.update({
                    name: element[0].name,
                    value: element[0].value
                });
            });
        };
    });

iframe.html

   window.update = function(data) {
        $scope.$apply(function() {
            $scope[data.name] = (data.value.length > 0) ? data.value: defaults[data.name];
        });
    };

Check this link: http://plnkr.co/edit/TGRj2o?p=preview

0

You need also $sce.trustAsResourceUrl or it won't open the website inside the iframe:

angular.module('myApp', [])
    .controller('dummy', ['$scope', '$sce', function ($scope, $sce) {

    $scope.url = $sce.trustAsResourceUrl('https://www.angularjs.org');

    $scope.changeIt = function () {
        $scope.url = $sce.trustAsResourceUrl('https://docs.angularjs.org/tutorial');
    }
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="myApp" ng-controller="dummy">
    <iframe ng-src="{{url}}" width="300" height="200"></iframe>
    <br>
    <button ng-click="changeIt()">Change it</button>
</div>
Abdo-Host
  • 2,470
  • 34
  • 33