diff --git a/src/ng/directive/attrs.js b/src/ng/directive/attrs.js index 1b646ff5d4c3..cd0990e60477 100644 --- a/src/ng/directive/attrs.js +++ b/src/ng/directive/attrs.js @@ -408,7 +408,7 @@ forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) { // ng-src, ng-srcset, ng-href are interpolated forEach(['src', 'srcset', 'href'], function(attrName) { var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { + ngAttributeAliasDirectives[normalized] = ['$sce', function($sce) { return { priority: 99, // it needs to run after the attributes are interpolated link: function(scope, element, attr) { @@ -422,6 +422,10 @@ forEach(['src', 'srcset', 'href'], function(attrName) { propName = null; } + // We need to sanitize the url at least once, in case it is a constant + // non-interpolated attribute. + attr.$set(normalized, $sce.getTrustedMediaUrl(attr[normalized])); + attr.$observe(normalized, function(value) { if (!value) { if (attrName === 'href') { @@ -441,5 +445,5 @@ forEach(['src', 'srcset', 'href'], function(attrName) { }); } }; - }; + }]; }); diff --git a/src/ng/interpolate.js b/src/ng/interpolate.js index 77b863ddcba9..955b816c202b 100644 --- a/src/ng/interpolate.js +++ b/src/ng/interpolate.js @@ -242,7 +242,7 @@ function $InterpolateProvider() { // Provide a quick exit and simplified result function for text with no interpolation if (!text.length || text.indexOf(startSymbol) === -1) { - if (mustHaveExpression && !contextAllowsConcatenation) return; + if (mustHaveExpression) return; var unescapedText = unescapeText(text); if (contextAllowsConcatenation) { diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 2dd80560509c..92fc545f3139 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -3577,6 +3577,15 @@ describe('$compile', function() { }) ); + it('should support non-interpolated `src` and `data-src` on the same element', + inject(function($rootScope, $compile) { + var element = $compile('')($rootScope); + expect(element.attr('src')).toEqual('abc'); + expect(element.attr('data-src')).toEqual('123'); + $rootScope.$digest(); + expect(element.attr('src')).toEqual('abc'); + expect(element.attr('data-src')).toEqual('123'); + })); it('should call observer only when the attribute value changes', function() { module(function() { @@ -12084,6 +12093,49 @@ describe('$compile', function() { expect(element.attr('test6')).toBe('Misko'); })); + describe('with media url attributes', function() { + it('should work with interpolated ng-attr-src', inject(function() { + $rootScope.name = 'some-image.png'; + element = $compile('')($rootScope); + expect(element.attr('src')).toBeUndefined(); + + $rootScope.$digest(); + expect(element.attr('src')).toBe('some-image.png'); + + $rootScope.name = 'other-image.png'; + $rootScope.$digest(); + expect(element.attr('src')).toBe('other-image.png'); + })); + + it('should work with interpolated ng-attr-data-src', inject(function() { + $rootScope.name = 'some-image.png'; + element = $compile('')($rootScope); + expect(element.attr('data-src')).toBeUndefined(); + + $rootScope.$digest(); + expect(element.attr('data-src')).toBe('some-image.png'); + + $rootScope.name = 'other-image.png'; + $rootScope.$digest(); + expect(element.attr('data-src')).toBe('other-image.png'); + })); + + it('should work alongside constant [src]-attribute and [ng-attr-data-src] attributes', inject(function() { + $rootScope.name = 'some-image.png'; + element = $compile('')($rootScope); + expect(element.attr('data-src')).toBeUndefined(); + + $rootScope.$digest(); + expect(element.attr('src')).toBe('constant.png'); + expect(element.attr('data-src')).toBe('some-image.png'); + + $rootScope.name = 'other-image.png'; + $rootScope.$digest(); + expect(element.attr('src')).toBe('constant.png'); + expect(element.attr('data-src')).toBe('other-image.png'); + })); + }); + describe('when an attribute has a dash-separated name', function() { it('should work with different prefixes', inject(function() { $rootScope.name = 'JamieMason'; diff --git a/test/ng/directive/ngSrcSpec.js b/test/ng/directive/ngSrcSpec.js index 5d2a067026b8..fcc48f292afd 100644 --- a/test/ng/directive/ngSrcSpec.js +++ b/test/ng/directive/ngSrcSpec.js @@ -78,6 +78,20 @@ describe('ngSrc', function() { expect(element.prop('src')).toEqual('http://somewhere/abc'); })); } + + it('should work with `src` attribute on the same element', inject(function($rootScope, $compile) { + $rootScope.imageUrl = 'dynamic'; + element = $compile('')($rootScope); + expect(element.attr('src')).toBe('static'); + $rootScope.$digest(); + expect(element.attr('src')).toBe('dynamic'); + dealoc(element); + + element = $compile('')($rootScope); + expect(element.attr('src')).toBe('static'); + $rootScope.$digest(); + expect(element.attr('src')).toBe('dynamic'); + })); }); describe('iframe[ng-src]', function() {