X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class DirectiveParser implements CompileStep {
if (isPresent(bindingAst)) {
directiveBinderBuilder.bindProperty(dirProperty, bindingAst);
}
compileElement.bindElement().bindPropertyToDirective(dashCaseToCamelCase(elProp));
}

_bindDirectiveEvent(eventName, action, compileElement, directiveBinderBuilder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const CLASS_PREFIX = 'class.';
const STYLE_PREFIX = 'style.';

export class PropertySetterFactory {
private static _noopSetter(el, value) {}
static noopSetter(el, value) {}

private _lazyPropertySettersCache: StringMap<string, Function> = StringMapWrapper.create();
private _eagerPropertySettersCache: StringMap<string, Function> = StringMapWrapper.create();
Expand Down Expand Up @@ -69,7 +69,7 @@ export class PropertySetterFactory {
if (DOM.hasProperty(protoElement, property)) {
setterFn = reflector.setter(property);
} else {
setterFn = PropertySetterFactory._noopSetter;
setterFn = PropertySetterFactory.noopSetter;
}
StringMapWrapper.set(this._eagerPropertySettersCache, property, setterFn);
}
Expand Down
20 changes: 17 additions & 3 deletions modules/angular2/src/render/dom/view/proto_view_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,17 @@ export class ProtoViewBuilder {
});

MapWrapper.forEach(ebb.propertyBindings, (_, propertyName) => {
var propSetter =
setterFactory.createSetter(ebb.element, isPresent(ebb.componentId), propertyName);

propertySetters.set(
propertyName,
setterFactory.createSetter(ebb.element, isPresent(ebb.componentId), propertyName));
if (propSetter === PropertySetterFactory.noopSetter) {
if (!SetWrapper.has(ebb.propertyBindingsToDirectives, propertyName)) {
throw new BaseException(
`Can't bind to '${propertyName}' since it isn't a know property of the '${DOM.tagName(ebb.element).toLowerCase()}' element and there are no matching directives with a corresponding property`);
}
}

propertySetters.set(propertyName, propSetter);
});

var nestedProtoView =
Expand Down Expand Up @@ -170,6 +177,7 @@ export class ElementBinderBuilder {
nestedProtoView: ProtoViewBuilder = null;
propertyBindings: Map<string, ASTWithSource> = new Map();
variableBindings: Map<string, string> = new Map();
propertyBindingsToDirectives: Set<string> = new Set();
eventBindings: List<api.EventBinding> = [];
eventBuilder: EventBuilder = new EventBuilder();
textBindingNodes: List</*node*/ any> = [];
Expand Down Expand Up @@ -210,6 +218,12 @@ export class ElementBinderBuilder {

bindProperty(name, expression) { this.propertyBindings.set(name, expression); }

bindPropertyToDirective(name: string) {
// we are filling in a set of property names that are bound to a property
// of at least one directive. This allows us to report "dangling" bindings.
this.propertyBindingsToDirectives.add(name);
}

bindVariable(name, value) {
// When current is a view root, the variable bindings are set to the *nested* proto view.
// The root view conceptually signifies a new "block scope" (the nested view), to which
Expand Down
59 changes: 32 additions & 27 deletions modules/angular2/test/core/compiler/integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,22 +192,6 @@ export function main() {
});
}));

it('should ignore bindings to unknown properties',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
tb.overrideView(MyComp,
new viewAnn.View({template: '<div unknown="{{ctxProp}}"></div>'}));

tb.createView(MyComp, {context: ctx})
.then((view) => {

ctx.ctxProp = 'Some value';
view.detectChanges();
expect(DOM.hasProperty(view.rootNodes[0], 'unknown')).toBeFalsy();

async.done();
});
}));

it('should consume directive watch expression change.',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
var tpl = '<div>' +
Expand Down Expand Up @@ -247,7 +231,7 @@ export function main() {
it("should support pipes in bindings",
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
tb.overrideView(MyComp, new viewAnn.View({
template: '<div [my-dir] #dir="mydir" [elprop]="ctxProp | double"></div>',
template: '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>',
directives: [MyDir]
}));

Expand Down Expand Up @@ -442,7 +426,7 @@ export function main() {
it('should assign a directive to a var-',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
tb.overrideView(MyComp, new viewAnn.View({
template: '<p><div [export-dir] #localdir="dir"></div></p>',
template: '<p><div export-dir #localdir="dir"></div></p>',
directives: [ExportDir]
}));

Expand Down Expand Up @@ -1144,7 +1128,7 @@ export function main() {
it('should specify a location of an error that happened during change detection (element property)',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {

tb.overrideView(MyComp, new viewAnn.View({template: '<div [prop]="a.b"></div>'}));
tb.overrideView(MyComp, new viewAnn.View({template: '<div [title]="a.b"></div>'}));

tb.createView(MyComp, {context: ctx})
.then((view) => {
Expand All @@ -1157,10 +1141,10 @@ export function main() {
it('should specify a location of an error that happened during change detection (directive property)',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {

tb.overrideView(
MyComp,
new viewAnn.View(
{template: '<child-cmp [prop]="a.b"></child-cmp>', directives: [ChildComp]}));
tb.overrideView(MyComp, new viewAnn.View({
template: '<child-cmp [dir-prop]="a.b"></child-cmp>',
directives: [ChildComp]
}));

tb.createView(MyComp, {context: ctx})
.then((view) => {
Expand Down Expand Up @@ -1205,6 +1189,30 @@ export function main() {
});
}));

describe('Missing property bindings', () => {
it('should throw on bindings to unknown properties',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
tb.overrideView(MyComp,
new viewAnn.View({template: '<div unknown="{{ctxProp}}"></div>'}));

PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => {
expect(e.message).toEqual(
`Can't bind to 'unknown' since it isn't a know property of the 'div' element and there are no matching directives with a corresponding property`);
async.done();
return null;
});
}));

it('should not throw for property binding to a non-existing property when there is a matching directive property',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
tb.overrideView(
MyComp,
new viewAnn.View(
{template: '<div my-dir [elprop]="ctxProp"></div>', directives: [MyDir]}));
tb.createView(MyComp, {context: ctx}).then((val) => { async.done(); });
}));
});

// Disabled until a solution is found, refs:
// - https://github.com/angular/angular/issues/776
// - https://github.com/angular/angular/commit/81f3f32
Expand Down Expand Up @@ -1353,10 +1361,7 @@ class ComponentWithPipes {
prop: string;
}

@Component({
selector: 'child-cmp',
appInjector: [MyService],
})
@Component({selector: 'child-cmp', properties: ['dirProp'], appInjector: [MyService]})
@View({directives: [MyDir], template: '{{ctxProp}}'})
@Injectable()
class ChildComp {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function main() {
tb.overrideView(
MyComp,
new viewAnn.View(
{template: '<div [field]="123" [lifecycle]></div>', directives: [LifecycleDir]}));
{template: '<div [field]="123" lifecycle></div>', directives: [LifecycleDir]}));

tb.createView(MyComp, {context: ctx})
.then((view) => {
Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/test/directives/ng_for_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export function main() {

it('should repeat over nested arrays with no intermediate element',
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
var template = '<div><template [ng-for] #item [ng-for-of]="items">' +
var template = '<div><template ng-for #item [ng-for-of]="items">' +
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having [ng-for] with no expression should be an error:

A property binding [ng-for] must have an expression associated with it.

Could we add a test?

'<div template="ng-for #subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div></template></div>';
Expand Down
6 changes: 3 additions & 3 deletions modules/angular2/test/directives/ng_switch_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ export function main() {
'<template [ng-switch-when]="\'b\'"><li>when b1;</li></template>' +
'<template [ng-switch-when]="\'a\'"><li>when a2;</li></template>' +
'<template [ng-switch-when]="\'b\'"><li>when b2;</li></template>' +
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be able to change this to: ng-swich-when="b" Could you do that for consistency?

'<template [ng-switch-default]><li>when default1;</li></template>' +
'<template [ng-switch-default]><li>when default2;</li></template>' +
'<template ng-switch-default><li>when default1;</li></template>' +
'<template ng-switch-default><li>when default2;</li></template>' +
'</ul></div>';

tb.createView(TestComponent, {html: template})
Expand Down Expand Up @@ -108,7 +108,7 @@ export function main() {
'<ul [ng-switch]="switchValue">' +
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here drop []

'<template [ng-switch-when]="when1"><li>when 1;</li></template>' +
'<template [ng-switch-when]="when2"><li>when 2;</li></template>' +
'<template [ng-switch-default]><li>when default;</li></template>' +
'<template ng-switch-default><li>when default;</li></template>' +
'</ul></div>';

tb.createView(TestComponent, {html: template})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ var conditionalContentComponent = DirectiveMetadata.create({
});

var autoViewportDirective = DirectiveMetadata.create(
{selector: '[auto]', id: '[auto]', type: DirectiveMetadata.DIRECTIVE_TYPE});
{selector: '[auto]', id: 'auto', properties: ['auto'], type: DirectiveMetadata.DIRECTIVE_TYPE});

var tabComponent =
DirectiveMetadata.create({selector: 'tab', id: 'tab', type: DirectiveMetadata.COMPONENT_TYPE});
Expand Down
2 changes: 1 addition & 1 deletion modules/benchmarks/src/costs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class DynamicDummy {
</div>
<div *ng-if="testingWithDirectives">
<dummy [dummy-decorator] *ng-for="#i of list"></dummy>
<dummy dummy-decorator *ng-for="#i of list"></dummy>
</div>
<div *ng-if="testingDynamicComponents">
Expand Down
4 changes: 2 additions & 2 deletions modules/benchmarks/src/largetable/largetable_benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ class CellData {
</tbody>
<tbody template="ng-switch-when 'interpolationAttr'">
<tr template="ng-for #row of data">
<td template="ng-for #column of row" i="{{column.i}}" j="{{column.j}}">
<td template="ng-for #column of row" attr.i="{{column.i}}" attr.j="{{column.j}}">
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tbosch I hope that this doesn't change the nature of this test too much - I had to modify this one as we don't support binding to non-existing properties after this change.

i,j attrs
</td>
</tr>
Expand Down Expand Up @@ -269,7 +269,7 @@ class LargetableComponent {
@Component({selector: 'app'})
@View({
directives: [LargetableComponent],
template: `<largetable [data]='data' [benchmarkType]='benchmarkType'></largetable>`
template: `<largetable [data]='data' [benchmark-type]='benchmarkType'></largetable>`
})
class AppComponent {
data;
Expand Down
X Tutup