fix(compiler): merge `class` and `style` attributes from the element with the host attributes
Closes #4583 Closes #4680
This commit is contained in:
parent
da1272f368
commit
eacc8e3803
|
@ -1,4 +1,4 @@
|
||||||
import {isPresent, isBlank, Type, isString} from 'angular2/src/core/facade/lang';
|
import {isPresent, isBlank, Type, isString, StringWrapper} from 'angular2/src/core/facade/lang';
|
||||||
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {
|
import {
|
||||||
TemplateCmd,
|
TemplateCmd,
|
||||||
|
@ -44,6 +44,8 @@ export var TEMPLATE_COMMANDS_MODULE_REF =
|
||||||
moduleRef(`package:angular2/src/core/linker/template_commands${MODULE_SUFFIX}`);
|
moduleRef(`package:angular2/src/core/linker/template_commands${MODULE_SUFFIX}`);
|
||||||
|
|
||||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
|
const CLASS_ATTR = 'class';
|
||||||
|
const STYLE_ATTR = 'style';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CommandCompiler {
|
export class CommandCompiler {
|
||||||
|
@ -211,14 +213,14 @@ class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||||
|
|
||||||
private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
|
private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
|
||||||
attrAsts: TemplateAst[]): string[] {
|
attrAsts: TemplateAst[]): string[] {
|
||||||
var attrNameAndValues: string[] = visitAndReturnContext(this, attrAsts, []);
|
var attrs = keyValueArrayToMap(visitAndReturnContext(this, attrAsts, []));
|
||||||
directives.forEach(directiveMeta => {
|
directives.forEach(directiveMeta => {
|
||||||
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
||||||
attrNameAndValues.push(name);
|
var prevValue = attrs[name];
|
||||||
attrNameAndValues.push(value);
|
attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return removeKeyValueArrayDuplicates(attrNameAndValues);
|
return mapToKeyValueArray(attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, context: any): any {
|
visitNgContent(ast: NgContentAst, context: any): any {
|
||||||
|
@ -328,6 +330,36 @@ function removeKeyValueArrayDuplicates(keyValueArray: string[]): string[] {
|
||||||
return resultKeyValueArray;
|
return resultKeyValueArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function keyValueArrayToMap(keyValueArr: string[]): {[key: string]: string} {
|
||||||
|
var data: {[key: string]: string} = {};
|
||||||
|
for (var i = 0; i < keyValueArr.length; i += 2) {
|
||||||
|
data[keyValueArr[i]] = keyValueArr[i + 1];
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapToKeyValueArray(data: {[key: string]: string}): string[] {
|
||||||
|
var entryArray = [];
|
||||||
|
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
|
||||||
|
// We need to sort to get a defined output order
|
||||||
|
// for tests and for caching generated artifacts...
|
||||||
|
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
|
||||||
|
var keyValueArray = [];
|
||||||
|
entryArray.forEach((entry) => {
|
||||||
|
keyValueArray.push(entry[0]);
|
||||||
|
keyValueArray.push(entry[1]);
|
||||||
|
});
|
||||||
|
return keyValueArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
||||||
|
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
||||||
|
return `${attrValue1} ${attrValue2}`;
|
||||||
|
} else {
|
||||||
|
return attrValue2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DirectiveContext {
|
class DirectiveContext {
|
||||||
constructor(public index: number, public eventTargetAndNames: string[],
|
constructor(public index: number, public eventTargetAndNames: string[],
|
||||||
public targetVariableNameAndValues: any[],
|
public targetVariableNameAndValues: any[],
|
||||||
|
|
|
@ -218,6 +218,48 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should merge element attributes with host attributes',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var rootComp = createComp({
|
||||||
|
type: RootCompTypeMeta,
|
||||||
|
template: '<div class="origclass" style="origstyle" role="origrole" attr1>'
|
||||||
|
});
|
||||||
|
var dir = CompileDirectiveMetadata.create({
|
||||||
|
selector: 'div',
|
||||||
|
isComponent: false,
|
||||||
|
type: SomeDirTypeMeta,
|
||||||
|
host: {'class': 'newclass', 'style': 'newstyle', 'role': 'newrole', 'attr2': ''}
|
||||||
|
});
|
||||||
|
run(rootComp, [dir])
|
||||||
|
.then((data) => {
|
||||||
|
expect(data).toEqual([
|
||||||
|
[
|
||||||
|
BEGIN_ELEMENT,
|
||||||
|
'div',
|
||||||
|
[
|
||||||
|
'attr1',
|
||||||
|
'',
|
||||||
|
'attr2',
|
||||||
|
'',
|
||||||
|
'class',
|
||||||
|
'origclass newclass',
|
||||||
|
'role',
|
||||||
|
'newrole',
|
||||||
|
'style',
|
||||||
|
'origstyle newstyle'
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
['SomeDirType'],
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
[END_ELEMENT]
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should emulate style encapsulation', inject([AsyncTestCompleter], (async) => {
|
it('should emulate style encapsulation', inject([AsyncTestCompleter], (async) => {
|
||||||
var rootComp = createComp({
|
var rootComp = createComp({
|
||||||
type: RootCompTypeMeta,
|
type: RootCompTypeMeta,
|
||||||
|
|
Loading…
Reference in New Issue