feat(ivy): adding support for ngNonBindable attribute

This commit is contained in:
Andrew Kushnir
2018-09-26 13:19:04 -07:00
committed by Alex Rickabaugh
parent eeebe28c0f
commit b286abeabe
9 changed files with 256 additions and 4 deletions

View File

@ -84,6 +84,8 @@ export {
elementProperty as ɵelementProperty,
projectionDef as ɵprojectionDef,
reference as ɵreference,
setBindingsEnabled as ɵsetBindingsEnabled,
setBindingsDisabled as ɵsetBindingsDisabled,
elementAttribute as ɵelementAttribute,
elementStyling as ɵelementStyling,
elementStylingMap as ɵelementStylingMap,

View File

@ -67,6 +67,9 @@ export {
namespaceMathML,
namespaceSVG,
setBindingsEnabled,
setBindingsDisabled,
projection,
projectionDef,

View File

@ -99,6 +99,8 @@ export function getCurrentSanitizer(): Sanitizer|null {
*/
let elementDepthCount !: number;
let bindingsEnabled !: boolean;
/**
* Returns the current OpaqueViewState instance.
*
@ -538,6 +540,7 @@ export function resetComponentState() {
isParent = false;
previousOrParentTNode = null !;
elementDepthCount = 0;
bindingsEnabled = true;
}
/**
@ -862,6 +865,7 @@ function nativeNodeLocalRefExtractor(tNode: TNode, currentView: LViewData): RNod
function createDirectivesAndLocals(
localRefs: string[] | null | undefined,
localRefExtractor: LocalRefExtractor = nativeNodeLocalRefExtractor) {
if (!bindingsEnabled) return;
if (firstTemplatePass) {
ngDevMode && ngDevMode.firstTemplatePass++;
cacheMatchingDirectivesForNode(previousOrParentTNode, tView, localRefs || null);
@ -1394,6 +1398,22 @@ export function elementProperty<T>(
}
}
/**
* Enables bindings processing for further instructions
* (used while processing "ngNonBindable" element's attribute).
*/
export function setBindingsEnabled(): void {
bindingsEnabled = true;
}
/**
* Disables bindings processing for further instructions
* (used while processing "ngNonBindable" element's attribute).
*/
export function setBindingsDisabled(): void {
bindingsEnabled = false;
}
/**
* Constructs a TNode object from the arguments.
*

View File

@ -46,6 +46,8 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵnamespaceHTML': r3.namespaceHTML,
'ɵnamespaceMathML': r3.namespaceMathML,
'ɵnamespaceSVG': r3.namespaceSVG,
'ɵsetBindingsEnabled': r3.setBindingsEnabled,
'ɵsetBindingsDisabled': r3.setBindingsDisabled,
'ɵelementStart': r3.elementStart,
'ɵelementEnd': r3.elementEnd,
'ɵelement': r3.element,

View File

@ -7,12 +7,12 @@
*/
import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {RenderFlags} from '@angular/core/src/render3';
import {reference, RenderFlags} from '@angular/core/src/render3';
import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, setBindingsEnabled, setBindingsDisabled, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions';
import {InitialStylingFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3, RText, RComment, RNode, RendererStyleFlags3, ProceduralRenderer3} from '../../src/render3/interfaces/renderer';
import {HEADER_OFFSET, CONTEXT, DIRECTIVES} from '../../src/render3/interfaces/view';
@ -132,6 +132,132 @@ describe('render3 integration test', () => {
});
describe('ngNonBindable handling', () => {
it('should keep local ref for host element', () => {
/**
* <b ngNonBindable #myRef id="my-id">
* <i>Hello {{ name }}!</i>
* </b>
* {{ myRef.id }}
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'b', ['id', 'my-id'], ['myRef', '']);
setBindingsDisabled();
elementStart(2, 'i');
text(3, 'Hello {{ name }}!');
elementEnd();
setBindingsEnabled();
elementEnd();
text(4);
}
if (rf & RenderFlags.Update) {
const ref = reference(1) as any;
textBinding(4, interpolation1(" ", ref.id, " "));
}
}, 5, 1);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<b id="my-id"><i>Hello {{ name }}!</i></b> my-id ');
});
it('should not have local refs for nested elements', () => {
/**
* <div ngNonBindable>
* <input value="one" #myInput> {{ myInput.value }}
* </div>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'div');
setBindingsDisabled();
element(1, 'input', ['value', 'one']);
text(2, '{{ myInput.value }}');
setBindingsEnabled();
elementEnd();
}
}, 3, 0);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<div><input value="one">{{ myInput.value }}</div>');
});
it('should invoke directives for host element', () => {
let directiveInvoked: boolean = false;
class TestDirective {
ngOnInit() {
directiveInvoked = true;
}
static ngDirectiveDef = defineDirective({
type: TestDirective,
selectors: [['', 'directive', '']],
factory: () => new TestDirective()
});
}
/**
* <b ngNonBindable directive>
* <i>Hello {{ name }}!</i>
* </b>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'b', ['directive', '']);
setBindingsDisabled();
elementStart(1, 'i');
text(2, 'Hello {{ name }}!');
elementEnd();
setBindingsEnabled();
elementEnd();
}
}, 3, 0, [TestDirective]);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<b directive=""><i>Hello {{ name }}!</i></b>');
expect(directiveInvoked).toEqual(true);
});
it('should not invoke directives for nested elements', () => {
let directiveInvoked: boolean = false;
class TestDirective {
ngOnInit() {
directiveInvoked = true;
}
static ngDirectiveDef = defineDirective({
type: TestDirective,
selectors: [['', 'directive', '']],
factory: () => new TestDirective()
});
}
/**
* <b ngNonBindable>
* <i directive>Hello {{ name }}!</i>
* </b>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'b');
setBindingsDisabled();
elementStart(1, 'i', ['directive', '']);
text(2, 'Hello {{ name }}!');
elementEnd();
setBindingsEnabled();
elementEnd();
}
}, 3, 0, [TestDirective]);
const fixture = new ComponentFixture(App);
expect(fixture.html).toEqual('<b><i directive="">Hello {{ name }}!</i></b>');
expect(directiveInvoked).toEqual(false);
});
});
describe('Siblings update', () => {
it('should handle a flat list of static/bound text nodes', () => {
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {