Skip to content

Commit

Permalink
fix($compile): allow the usage of "$" in isolate scope property alias
Browse files Browse the repository at this point in the history
Previously, when using an alias for an isolate scope or `bindings` property
(e.g. `alias: '<attrName'` instead of `attrName: '<'`), a `$compile:iscp` error
was thrown if the attribute name contained a "$".
This commit removes the error by changing the regex to allow "$" characters in
the attribute name when using a property alias.

Fixes: angular#15586

Closes angular#15594
  • Loading branch information
frederikprijck authored and ellimist committed Mar 15, 2017
1 parent 83f4a7b commit ed90eeb
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/ng/compile.js
Expand Up @@ -983,7 +983,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var bindingCache = createMap();

function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;

var bindings = createMap();

Expand Down
69 changes: 55 additions & 14 deletions test/ng/compileSpec.js
Expand Up @@ -4750,21 +4750,28 @@ describe('$compile', function() {
scope: {
attr: '@',
attrAlias: '@attr',
$attrAlias: '@$attr$',
ref: '=',
refAlias: '= ref',
$refAlias: '= $ref$',
reference: '=',
optref: '=?',
optrefAlias: '=? optref',
$optrefAlias: '=? $optref$',
optreference: '=?',
colref: '=*',
colrefAlias: '=* colref',
$colrefAlias: '=* $colref$',
owRef: '<',
owRefAlias: '< owRef',
$owRefAlias: '< $owRef$',
owOptref: '<?',
owOptrefAlias: '<? owOptref',
$owOptrefAlias: '<? $owOptref$',
expr: '&',
optExpr: '&?',
exprAlias: '&expr',
$exprAlias: '&$expr$',
constructor: '&?'
},
link: function(scope) {
Expand Down Expand Up @@ -5183,76 +5190,85 @@ describe('$compile', function() {

describe('attribute', function() {
it('should copy simple attribute', inject(function() {
compile('<div><span my-component attr="some text">');
compile('<div><span my-component attr="some text" $attr$="some other text">');

expect(componentScope.attr).toEqual('some text');
expect(componentScope.attrAlias).toEqual('some text');
expect(componentScope.$attrAlias).toEqual('some other text');
expect(componentScope.attrAlias).toEqual(componentScope.attr);
}));

it('should copy an attribute with spaces', inject(function() {
compile('<div><span my-component attr=" some text ">');
compile('<div><span my-component attr=" some text " $attr$=" some other text ">');

expect(componentScope.attr).toEqual(' some text ');
expect(componentScope.attrAlias).toEqual(' some text ');
expect(componentScope.$attrAlias).toEqual(' some other text ');
expect(componentScope.attrAlias).toEqual(componentScope.attr);
}));

it('should set up the interpolation before it reaches the link function', inject(function() {
$rootScope.name = 'misko';
compile('<div><span my-component attr="hello {{name}}">');
compile('<div><span my-component attr="hello {{name}}" $attr$="hi {{name}}">');
expect(componentScope.attr).toEqual('hello misko');
expect(componentScope.attrAlias).toEqual('hello misko');
expect(componentScope.$attrAlias).toEqual('hi misko');
}));

it('should update when interpolated attribute updates', inject(function() {
compile('<div><span my-component attr="hello {{name}}">');
compile('<div><span my-component attr="hello {{name}}" $attr$="hi {{name}}">');

$rootScope.name = 'igor';
$rootScope.$apply();

expect(componentScope.attr).toEqual('hello igor');
expect(componentScope.attrAlias).toEqual('hello igor');
expect(componentScope.$attrAlias).toEqual('hi igor');
}));
});


describe('object reference', function() {
it('should update local when origin changes', inject(function() {
compile('<div><span my-component ref="name">');
compile('<div><span my-component ref="name" $ref$="name">');
expect(componentScope.ref).toBeUndefined();
expect(componentScope.refAlias).toBe(componentScope.ref);
expect(componentScope.$refAlias).toBe(componentScope.ref);

$rootScope.name = 'misko';
$rootScope.$apply();

expect($rootScope.name).toBe('misko');
expect(componentScope.ref).toBe('misko');
expect(componentScope.refAlias).toBe('misko');
expect(componentScope.$refAlias).toBe('misko');

$rootScope.name = {};
$rootScope.$apply();
expect(componentScope.ref).toBe($rootScope.name);
expect(componentScope.refAlias).toBe($rootScope.name);
expect(componentScope.$refAlias).toBe($rootScope.name);
}));


it('should update local when both change', inject(function() {
compile('<div><span my-component ref="name">');
compile('<div><span my-component ref="name" $ref$="name">');
$rootScope.name = {mark:123};
componentScope.ref = 'misko';

$rootScope.$apply();
expect($rootScope.name).toEqual({mark:123});
expect(componentScope.ref).toBe($rootScope.name);
expect(componentScope.refAlias).toBe($rootScope.name);
expect(componentScope.$refAlias).toBe($rootScope.name);

$rootScope.name = 'igor';
componentScope.ref = {};
$rootScope.$apply();
expect($rootScope.name).toEqual('igor');
expect(componentScope.ref).toBe($rootScope.name);
expect(componentScope.refAlias).toBe($rootScope.name);
expect(componentScope.$refAlias).toBe($rootScope.name);
}));

it('should not break if local and origin both change to the same value', inject(function() {
Expand Down Expand Up @@ -5382,26 +5398,30 @@ describe('$compile', function() {

describe('optional object reference', function() {
it('should update local when origin changes', inject(function() {
compile('<div><span my-component optref="name">');
compile('<div><span my-component optref="name" $optref$="name">');
expect(componentScope.optRef).toBeUndefined();
expect(componentScope.optRefAlias).toBe(componentScope.optRef);
expect(componentScope.$optRefAlias).toBe(componentScope.optRef);

$rootScope.name = 'misko';
$rootScope.$apply();
expect(componentScope.optref).toBe($rootScope.name);
expect(componentScope.optrefAlias).toBe($rootScope.name);
expect(componentScope.$optrefAlias).toBe($rootScope.name);

$rootScope.name = {};
$rootScope.$apply();
expect(componentScope.optref).toBe($rootScope.name);
expect(componentScope.optrefAlias).toBe($rootScope.name);
expect(componentScope.$optrefAlias).toBe($rootScope.name);
}));

it('should not throw exception when reference does not exist', inject(function() {
compile('<div><span my-component>');

expect(componentScope.optref).toBeUndefined();
expect(componentScope.optrefAlias).toBeUndefined();
expect(componentScope.$optrefAlias).toBeUndefined();
expect(componentScope.optreference).toBeUndefined();
}));
});
Expand All @@ -5419,16 +5439,18 @@ describe('$compile', function() {
$rootScope.query = '';
$rootScope.$apply();

compile('<div><span my-component colref="collection | filter:query">');
compile('<div><span my-component colref="collection | filter:query" $colref$="collection | filter:query">');

expect(componentScope.colref).toEqual($rootScope.collection);
expect(componentScope.colrefAlias).toEqual(componentScope.colref);
expect(componentScope.$colrefAlias).toEqual(componentScope.colref);

$rootScope.query = 'Gab';
$rootScope.$apply();

expect(componentScope.colref).toEqual([$rootScope.collection[0]]);
expect(componentScope.colrefAlias).toEqual([$rootScope.collection[0]]);
expect(componentScope.$colrefAlias).toEqual([$rootScope.collection[0]]);
}));

it('should update origin scope when isolate scope changes', inject(function() {
Expand Down Expand Up @@ -5456,23 +5478,26 @@ describe('$compile', function() {

describe('one-way binding', function() {
it('should update isolate when the identity of origin changes', inject(function() {
compile('<div><span my-component ow-ref="obj">');
compile('<div><span my-component ow-ref="obj" $ow-ref$="obj">');

expect(componentScope.owRef).toBeUndefined();
expect(componentScope.owRefAlias).toBe(componentScope.owRef);
expect(componentScope.$owRefAlias).toBe(componentScope.owRef);

$rootScope.obj = {value: 'initial'};
$rootScope.$apply();

expect($rootScope.obj).toEqual({value: 'initial'});
expect(componentScope.owRef).toEqual({value: 'initial'});
expect(componentScope.owRefAlias).toBe(componentScope.owRef);
expect(componentScope.$owRefAlias).toBe(componentScope.owRef);

// This changes in both scopes because of reference
$rootScope.obj.value = 'origin1';
$rootScope.$apply();
expect(componentScope.owRef.value).toBe('origin1');
expect(componentScope.owRefAlias.value).toBe('origin1');
expect(componentScope.$owRefAlias.value).toBe('origin1');

componentScope.owRef = {value: 'isolate1'};
componentScope.$apply();
Expand All @@ -5483,17 +5508,19 @@ describe('$compile', function() {
$rootScope.$apply();
expect(componentScope.owRef.value).toBe('isolate1');
expect(componentScope.owRefAlias.value).toBe('origin2');
expect(componentScope.$owRefAlias.value).toBe('origin2');

// Change does propagate because object identity changes
$rootScope.obj = {value: 'origin3'};
$rootScope.$apply();
expect(componentScope.owRef.value).toBe('origin3');
expect(componentScope.owRef).toBe($rootScope.obj);
expect(componentScope.owRefAlias).toBe($rootScope.obj);
expect(componentScope.$owRefAlias).toBe($rootScope.obj);
}));

it('should update isolate when both change', inject(function() {
compile('<div><span my-component ow-ref="name">');
compile('<div><span my-component ow-ref="name" $ow-ref$="name">');

$rootScope.name = {mark:123};
componentScope.owRef = 'misko';
Expand All @@ -5502,13 +5529,15 @@ describe('$compile', function() {
expect($rootScope.name).toEqual({mark:123});
expect(componentScope.owRef).toBe($rootScope.name);
expect(componentScope.owRefAlias).toBe($rootScope.name);
expect(componentScope.$owRefAlias).toBe($rootScope.name);

$rootScope.name = 'igor';
componentScope.owRef = {};
$rootScope.$apply();
expect($rootScope.name).toEqual('igor');
expect(componentScope.owRef).toBe($rootScope.name);
expect(componentScope.owRefAlias).toBe($rootScope.name);
expect(componentScope.$owRefAlias).toBe($rootScope.name);
}));

describe('initialization', function() {
Expand Down Expand Up @@ -5705,17 +5734,19 @@ describe('$compile', function() {

it('should not update origin when identity of isolate changes', inject(function() {
$rootScope.name = {mark:123};
compile('<div><span my-component ow-ref="name">');
compile('<div><span my-component ow-ref="name" $ow-ref$="name">');

expect($rootScope.name).toEqual({mark:123});
expect(componentScope.owRef).toBe($rootScope.name);
expect(componentScope.owRefAlias).toBe($rootScope.name);
expect(componentScope.$owRefAlias).toBe($rootScope.name);

componentScope.owRef = 'martin';
$rootScope.$apply();
expect($rootScope.name).toEqual({mark: 123});
expect(componentScope.owRef).toBe('martin');
expect(componentScope.owRefAlias).toEqual({mark: 123});
expect(componentScope.$owRefAlias).toEqual({mark: 123});
}));


Expand Down Expand Up @@ -5862,45 +5893,51 @@ describe('$compile', function() {

describe('optional one-way binding', function() {
it('should update local when origin changes', inject(function() {
compile('<div><span my-component ow-optref="name">');
compile('<div><span my-component ow-optref="name" $ow-optref$="name">');

expect(componentScope.owOptref).toBeUndefined();
expect(componentScope.owOptrefAlias).toBe(componentScope.owOptref);
expect(componentScope.$owOptrefAlias).toBe(componentScope.owOptref);

$rootScope.name = 'misko';
$rootScope.$apply();
expect(componentScope.owOptref).toBe($rootScope.name);
expect(componentScope.owOptrefAlias).toBe($rootScope.name);
expect(componentScope.$owOptrefAlias).toBe($rootScope.name);

$rootScope.name = {};
$rootScope.$apply();
expect(componentScope.owOptref).toBe($rootScope.name);
expect(componentScope.owOptrefAlias).toBe($rootScope.name);
expect(componentScope.$owOptrefAlias).toBe($rootScope.name);
}));

it('should not throw exception when reference does not exist', inject(function() {
compile('<div><span my-component>');

expect(componentScope.owOptref).toBeUndefined();
expect(componentScope.owOptrefAlias).toBeUndefined();
expect(componentScope.$owOptrefAlias).toBeUndefined();
}));
});
});
});

describe('executable expression', function() {
it('should allow expression execution with locals', inject(function() {
compile('<div><span my-component expr="count = count + offset">');
compile('<div><span my-component expr="count = count + offset" $expr$="count = count + offset">');
$rootScope.count = 2;

expect(typeof componentScope.expr).toBe('function');
expect(typeof componentScope.exprAlias).toBe('function');
expect(typeof componentScope.$exprAlias).toBe('function');

expect(componentScope.expr({offset: 1})).toEqual(3);
expect($rootScope.count).toEqual(3);

expect(componentScope.exprAlias({offset: 10})).toEqual(13);
expect($rootScope.count).toEqual(13);
expect(componentScope.$exprAlias({offset: 10})).toEqual(23);
expect($rootScope.count).toEqual(23);
}));
});

Expand All @@ -5918,17 +5955,21 @@ describe('$compile', function() {
expect(componentScope.$$isolateBindings.attr.mode).toBe('@');
expect(componentScope.$$isolateBindings.attr.attrName).toBe('attr');
expect(componentScope.$$isolateBindings.attrAlias.attrName).toBe('attr');
expect(componentScope.$$isolateBindings.$attrAlias.attrName).toBe('$attr$');
expect(componentScope.$$isolateBindings.ref.mode).toBe('=');
expect(componentScope.$$isolateBindings.ref.attrName).toBe('ref');
expect(componentScope.$$isolateBindings.refAlias.attrName).toBe('ref');
expect(componentScope.$$isolateBindings.$refAlias.attrName).toBe('$ref$');
expect(componentScope.$$isolateBindings.reference.mode).toBe('=');
expect(componentScope.$$isolateBindings.reference.attrName).toBe('reference');
expect(componentScope.$$isolateBindings.owRef.mode).toBe('<');
expect(componentScope.$$isolateBindings.owRef.attrName).toBe('owRef');
expect(componentScope.$$isolateBindings.owRefAlias.attrName).toBe('owRef');
expect(componentScope.$$isolateBindings.$owRefAlias.attrName).toBe('$owRef$');
expect(componentScope.$$isolateBindings.expr.mode).toBe('&');
expect(componentScope.$$isolateBindings.expr.attrName).toBe('expr');
expect(componentScope.$$isolateBindings.exprAlias.attrName).toBe('expr');
expect(componentScope.$$isolateBindings.$exprAlias.attrName).toBe('$expr$');

var firstComponentScope = componentScope,
first$$isolateBindings = componentScope.$$isolateBindings;
Expand Down

0 comments on commit ed90eeb

Please sign in to comment.