feat(ivy): add ng-reflect debug text for containers (#27350)

PR Close #27350
This commit is contained in:
Olivier Combe 2018-11-29 16:39:43 +01:00 committed by Igor Minar
parent 20cef5078d
commit 1279a503a1
5 changed files with 53 additions and 41 deletions

View File

@ -16,7 +16,6 @@ import {Sanitizer} from '../sanitization/security';
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
import {Type} from '../type';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../util/ng_reflect';
import {assertDataInRange, assertDefined, assertEqual, assertHasParent, assertLessThan, assertNotEqual, assertPreviousIsParent} from './assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from './bindings';
import {attachPatchData, getComponentViewByInstance} from './context_discovery';
@ -30,7 +29,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, Pro
import {PlayerFactory} from './interfaces/player';
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {SanitizerFn} from './interfaces/sanitization';
import {StylingIndex} from './interfaces/styling';
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LView, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
@ -957,8 +956,10 @@ export function elementProperty<T>(
if (inputData && (dataValue = inputData[propName])) {
setInputsForProperty(lView, dataValue, value);
if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
if (ngDevMode && tNode.type === TNodeType.Element) {
setNgReflectProperties(lView, element as RElement, propName, value);
if (ngDevMode) {
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) {
setNgReflectProperties(lView, element, tNode.type, propName, value);
}
}
} else if (tNode.type === TNodeType.Element) {
const renderer = lView[RENDERER];
@ -1031,12 +1032,23 @@ function setInputsForProperty(lView: LView, inputs: PropertyAliasValue, value: a
}
}
function setNgReflectProperties(lView: LView, element: RElement, propName: string, value: any) {
function setNgReflectProperties(
lView: LView, element: RElement | RComment, type: TNodeType, propName: string, value: any) {
const renderer = lView[RENDERER];
const attrName = normalizeDebugBindingName(propName);
const debugValue = normalizeDebugBindingValue(value);
isProceduralRenderer(renderer) ? renderer.setAttribute(element, attrName, debugValue) :
element.setAttribute(attrName, debugValue);
if (type === TNodeType.Element) {
isProceduralRenderer(renderer) ?
renderer.setAttribute((element as RElement), attrName, debugValue) :
(element as RElement).setAttribute(attrName, debugValue);
} else if (value !== undefined) {
const value = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
if (isProceduralRenderer(renderer)) {
renderer.setValue((element as RComment), value);
} else {
(element as RComment).textContent = value;
}
}
}
/**

View File

@ -83,7 +83,7 @@ export interface ProceduralRenderer3 {
flags?: RendererStyleFlags2|RendererStyleFlags3): void;
removeStyle(el: RElement, style: string, flags?: RendererStyleFlags2|RendererStyleFlags3): void;
setProperty(el: RElement, name: string, value: any): void;
setValue(node: RText, value: string): void;
setValue(node: RText|RComment, value: string): void;
// TODO(misko): Deprecate in favor of addEventListener/removeEventListener
listen(target: RNode, eventName: string, callback: (event: any) => boolean | void): () => void;
@ -152,7 +152,7 @@ export interface RDomTokenList {
export interface RText extends RNode { textContent: string|null; }
export interface RComment extends RNode {}
export interface RComment extends RNode { textContent: string|null; }
// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.

View File

@ -1688,7 +1688,7 @@ function declareTests(config?: {useJit: boolean}) {
});
describe('logging property updates', () => {
fixmeIvy('FW-664: ng-reflect-* is not supported')
fixmeIvy('FW-587: Inputs with aliases in component decorators don\'t work')
.it('should reflect property values as attributes', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir]});
const template = '<div>' +
@ -1712,21 +1712,19 @@ function declareTests(config?: {useJit: boolean}) {
expect(getDOM().getInnerHTML(fixture.nativeElement)).toContain('ng-reflect-test_="hello"');
});
fixmeIvy('FW-664: ng-reflect-* is not supported')
.it('should reflect property values on template comments', () => {
const fixture =
TestBed.configureTestingModule({declarations: [MyComp]})
.overrideComponent(
MyComp,
{set: {template: '<ng-template [ngIf]="ctxBoolProp"></ng-template>'}})
.createComponent(MyComp);
it('should reflect property values on template comments', () => {
const fixture =
TestBed.configureTestingModule({declarations: [MyComp]})
.overrideComponent(
MyComp, {set: {template: '<ng-template [ngIf]="ctxBoolProp"></ng-template>'}})
.createComponent(MyComp);
fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
expect(getDOM().getInnerHTML(fixture.nativeElement))
.toContain('"ng\-reflect\-ng\-if"\: "true"');
});
expect(getDOM().getInnerHTML(fixture.nativeElement))
.toContain('"ng\-reflect\-ng\-if"\: "true"');
});
// also affected by FW-587: Inputs with aliases in component decorators don't work
fixmeIvy('FW-664: ng-reflect-* is not supported')

View File

@ -273,16 +273,19 @@ export function toHtml<T>(componentOrElement: T | RElement, keepNgReflect = fals
}
if (element) {
let html = stringifyElement(element)
.replace(/^<div host="">(.*)<\/div>$/, '$1')
.replace(/^<div fixture="mark">(.*)<\/div>$/, '$1')
.replace(/^<div host="mark">(.*)<\/div>$/, '$1')
.replace(' style=""', '')
.replace(/<!--container-->/g, '')
.replace(/<!--ng-container-->/g, '');
let html = stringifyElement(element);
if (!keepNgReflect) {
html = html.replace(/\sng-reflect-\S*="[^"]*"/g, '');
html = html.replace(/\sng-reflect-\S*="[^"]*"/g, '')
.replace(/<!--bindings=\{(\W.*\W\s*)?\}-->/g, '');
}
html = html.replace(/^<div host="">(.*)<\/div>$/, '$1')
.replace(/^<div fixture="mark">(.*)<\/div>$/, '$1')
.replace(/^<div host="mark">(.*)<\/div>$/, '$1')
.replace(' style=""', '')
.replace(/<!--container-->/g, '')
.replace(/<!--ng-container-->/g, '');
return html;
} else {
return '';

View File

@ -131,16 +131,15 @@ let lastCreatedRenderer: Renderer2;
checkSetters(fixture.componentRef, fixture.debugElement.children[0].nativeElement);
});
fixmeIvy('#FW-664 ng-reflect-* is not supported')
.it('should update any template comment property/attributes', () => {
const fixture =
TestBed.overrideTemplate(MyComp2, '<ng-container *ngIf="ctxBoolProp"></ng-container>')
.createComponent(MyComp2);
fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
const el = getRenderElement(fixture.nativeElement);
expect(getDOM().getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"');
});
it('should update any template comment property/attributes', () => {
const fixture =
TestBed.overrideTemplate(MyComp2, '<ng-container *ngIf="ctxBoolProp"></ng-container>')
.createComponent(MyComp2);
fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
const el = getRenderElement(fixture.nativeElement);
expect(getDOM().getInnerHTML(el)).toContain('"ng-reflect-ng-if": "true"');
});
it('should add and remove fragments', () => {
const fixture =