refactor(core): move DomAdapter style methods to ServerRenderer (#32408)

PR Close #32408
This commit is contained in:
Kara Erickson 2019-08-30 10:16:20 -07:00 committed by Miško Hevery
parent 970b58b13f
commit c207ad80fd
10 changed files with 46 additions and 100 deletions

View File

@ -50,11 +50,6 @@ export abstract class DomAdapter {
abstract createHtmlDocument(): HTMLDocument; abstract createHtmlDocument(): HTMLDocument;
abstract getDefaultDocument(): Document; abstract getDefaultDocument(): Document;
// Used by platform-server
abstract getStyle(element: any, styleName: string): any;
abstract setStyle(element: any, styleName: string, styleValue: string): any;
abstract removeStyle(element: any, styleName: string): any;
// Used by Title // Used by Title
abstract getTitle(doc: Document): string; abstract getTitle(doc: Document): string;
abstract setTitle(doc: Document, newTitle: string): any; abstract setTitle(doc: Document, newTitle: string): any;

View File

@ -36,27 +36,6 @@ import {isTextNode} from '@angular/platform-browser/testing/src/browser_util';
expect(() => getDOM().remove(d)).not.toThrow(); expect(() => getDOM().remove(d)).not.toThrow();
}); });
it('should parse styles with urls correctly', () => {
const d = getDOM().createElement('div');
getDOM().setStyle(d, 'background-url', 'url(http://test.com/bg.jpg)');
expect(getDOM().getStyle(d, 'background-url')).toBe('url(http://test.com/bg.jpg)');
});
// Test for regression caused by angular/angular#22536
it('should parse styles correctly following the spec', () => {
const d = getDOM().createElement('div');
getDOM().setStyle(d, 'background-image', 'url("paper.gif")');
expect(d.style.backgroundImage).toBe('url("paper.gif")');
expect(d.style.getPropertyValue('background-image')).toBe('url("paper.gif")');
expect(getDOM().getStyle(d, 'background-image')).toBe('url("paper.gif")');
});
it('should parse camel-case styles correctly', () => {
const d = getDOM().createElement('div');
getDOM().setStyle(d, 'marginRight', '10px');
expect(getDOM().getStyle(d, 'margin-right')).toBe('10px');
});
if (getDOM().supportsDOMEvents()) { if (getDOM().supportsDOMEvents()) {
describe('getBaseHref', () => { describe('getBaseHref', () => {
beforeEach(() => getDOM().resetBaseElement()); beforeEach(() => getDOM().resetBaseElement());

View File

@ -147,13 +147,11 @@ function declareTests(config?: {useJit: boolean}) {
fixture.componentInstance.ctxProp = '10'; fixture.componentInstance.ctxProp = '10';
fixture.detectChanges(); fixture.detectChanges();
expect(getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'height')) expect(fixture.debugElement.children[0].nativeElement.style['height']).toEqual('10px');
.toEqual('10px');
fixture.componentInstance.ctxProp = null !; fixture.componentInstance.ctxProp = null !;
fixture.detectChanges(); fixture.detectChanges();
expect(getDOM().getStyle(fixture.debugElement.children[0].nativeElement, 'height')) expect(fixture.debugElement.children[0].nativeElement.style['height']).toEqual('');
.toEqual('');
}); });
it('should consume binding to property names where attr name and property name do not match', it('should consume binding to property names where attr name and property name do not match',

View File

@ -247,12 +247,12 @@ function declareTests(config?: {useJit: boolean}) {
fixture.detectChanges(); fixture.detectChanges();
// In some browsers, this will contain the full background specification, not just // In some browsers, this will contain the full background specification, not just
// the color. // the color.
expect(getDOM().getStyle(e, 'background')).toMatch(/red.*/); expect(e.style['background']).toMatch(/red.*/);
ci.ctxProp = 'url(javascript:evil())'; ci.ctxProp = 'url(javascript:evil())';
fixture.detectChanges(); fixture.detectChanges();
// Updated value gets rejected, no value change. // Updated value gets rejected, no value change.
expect(getDOM().getStyle(e, 'background')).not.toContain('javascript'); expect(e.style['background']).not.toContain('javascript');
}); });
modifiedInIvy('Unknown property error thrown during update mode, not creation mode') modifiedInIvy('Unknown property error thrown during update mode, not creation mode')

View File

@ -164,8 +164,8 @@ const removeEventListener = '__zone_symbol__removeEventListener' as 'removeEvent
Services.checkAndUpdateView(view); Services.checkAndUpdateView(view);
const el = rootNodes[0]; const el = rootNodes[0];
expect(getDOM().getStyle(el, 'width')).toBe('10px'); expect(el.style['width']).toBe('10px');
expect(getDOM().getStyle(el, 'color')).toBe('red'); expect(el.style['color']).toBe('red');
}); });
}); });
}); });

View File

@ -119,16 +119,6 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
getElementsByTagName(element: any, name: string): HTMLElement[] { getElementsByTagName(element: any, name: string): HTMLElement[] {
return element.getElementsByTagName(name); return element.getElementsByTagName(name);
} }
setStyle(element: any, styleName: string, styleValue: string) {
element.style[styleName] = styleValue;
}
removeStyle(element: any, stylename: string) {
// IE requires '' instead of null
// see https://github.com/angular/angular/issues/7916
element.style[stylename] = '';
}
getStyle(element: any, stylename: string): string { return element.style[stylename]; }
getAttribute(element: Element, attribute: string): string|null { getAttribute(element: Element, attribute: string): string|null {
return element.getAttribute(attribute); return element.getAttribute(attribute);

View File

@ -109,55 +109,6 @@ export class DominoAdapter extends BrowserDomAdapter {
return href; return href;
} }
/** @internal */
_readStyleAttribute(element: any): {[name: string]: string} {
const styleMap: {[name: string]: string} = {};
const styleAttribute = element.getAttribute('style');
if (styleAttribute) {
const styleList = styleAttribute.split(/;+/g);
for (let i = 0; i < styleList.length; i++) {
const style = styleList[i].trim();
if (style.length > 0) {
const colonIndex = style.indexOf(':');
if (colonIndex === -1) {
throw new Error(`Invalid CSS style: ${style}`);
}
const name = style.substr(0, colonIndex).trim();
styleMap[name] = style.substr(colonIndex + 1).trim();
}
}
}
return styleMap;
}
/** @internal */
_writeStyleAttribute(element: any, styleMap: {[name: string]: string}) {
let styleAttrValue = '';
for (const key in styleMap) {
const newValue = styleMap[key];
if (newValue) {
styleAttrValue += key + ':' + styleMap[key] + ';';
}
}
element.setAttribute('style', styleAttrValue);
}
setStyle(element: any, styleName: string, styleValue?: string|null) {
styleName = styleName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
const styleMap = this._readStyleAttribute(element);
styleMap[styleName] = styleValue || '';
this._writeStyleAttribute(element, styleMap);
}
removeStyle(element: any, styleName: string) {
// IE requires '' instead of null
// see https://github.com/angular/angular/issues/7916
this.setStyle(element, styleName, '');
}
getStyle(element: any, styleName: string): string {
const styleMap = this._readStyleAttribute(element);
return styleMap[styleName] || '';
}
dispatchEvent(el: Node, evt: any) { dispatchEvent(el: Node, evt: any) {
el.dispatchEvent(evt); el.dispatchEvent(evt);

View File

@ -143,11 +143,16 @@ class DefaultServerRenderer2 implements Renderer2 {
removeClass(el: any, name: string): void { el.classList.remove(name); } removeClass(el: any, name: string): void { el.classList.remove(name); }
setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void { setStyle(el: any, style: string, value: any, flags: RendererStyleFlags2): void {
getDOM().setStyle(el, style, value); style = style.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
const styleMap = _readStyleAttribute(el);
styleMap[style] = value || '';
_writeStyleAttribute(el, styleMap);
} }
removeStyle(el: any, style: string, flags: RendererStyleFlags2): void { removeStyle(el: any, style: string, flags: RendererStyleFlags2): void {
getDOM().removeStyle(el, style); // IE requires '' instead of null
// see https://github.com/angular/angular/issues/7916
this.setStyle(el, style, '', flags);
} }
// The value was validated already as a property binding, against the property name. // The value was validated already as a property binding, against the property name.
@ -246,3 +251,34 @@ class EmulatedEncapsulationServerRenderer2 extends DefaultServerRenderer2 {
return el; return el;
} }
} }
function _readStyleAttribute(element: any): {[name: string]: string} {
const styleMap: {[name: string]: string} = {};
const styleAttribute = element.getAttribute('style');
if (styleAttribute) {
const styleList = styleAttribute.split(/;+/g);
for (let i = 0; i < styleList.length; i++) {
const style = styleList[i].trim();
if (style.length > 0) {
const colonIndex = style.indexOf(':');
if (colonIndex === -1) {
throw new Error(`Invalid CSS style: ${style}`);
}
const name = style.substr(0, colonIndex).trim();
styleMap[name] = style.substr(colonIndex + 1).trim();
}
}
}
return styleMap;
}
function _writeStyleAttribute(element: any, styleMap: {[name: string]: string}) {
let styleAttrValue = '';
for (const key in styleMap) {
const newValue = styleMap[key];
if (newValue) {
styleAttrValue += key + ':' + styleMap[key] + ';';
}
}
element.setAttribute('style', styleAttrValue);
}

View File

@ -53,9 +53,6 @@ export class WorkerDomAdapter extends DomAdapter {
createElement(tagName: any, doc?: any): HTMLElement { throw 'not implemented'; } createElement(tagName: any, doc?: any): HTMLElement { throw 'not implemented'; }
getHost(el: any): any { throw 'not implemented'; } getHost(el: any): any { throw 'not implemented'; }
getElementsByTagName(element: any, name: string): HTMLElement[] { throw 'not implemented'; } getElementsByTagName(element: any, name: string): HTMLElement[] { throw 'not implemented'; }
setStyle(element: any, styleName: string, styleValue: string) { throw 'not implemented'; }
removeStyle(element: any, styleName: string) { throw 'not implemented'; }
getStyle(element: any, styleName: string): string { throw 'not implemented'; }
getAttribute(element: any, attribute: string): string { throw 'not implemented'; } getAttribute(element: any, attribute: string): string { throw 'not implemented'; }
setAttribute(element: any, name: string, value: string) { throw 'not implemented'; } setAttribute(element: any, name: string, value: string) { throw 'not implemented'; }
createHtmlDocument(): HTMLDocument { throw 'not implemented'; } createHtmlDocument(): HTMLDocument { throw 'not implemented'; }

View File

@ -115,10 +115,10 @@ let lastCreatedRenderer: Renderer2;
expect(hasClass(el, 'a')).toBe(false); expect(hasClass(el, 'a')).toBe(false);
lastCreatedRenderer.setStyle(workerEl, 'width', '10px'); lastCreatedRenderer.setStyle(workerEl, 'width', '10px');
expect(getDOM().getStyle(el, 'width')).toEqual('10px'); expect(el.style['width']).toEqual('10px');
lastCreatedRenderer.removeStyle(workerEl, 'width'); lastCreatedRenderer.removeStyle(workerEl, 'width');
expect(getDOM().getStyle(el, 'width')).toEqual(''); expect(el.style['width']).toEqual('');
lastCreatedRenderer.setAttribute(workerEl, 'someattr', 'someValue'); lastCreatedRenderer.setAttribute(workerEl, 'someattr', 'someValue');
expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue'); expect(getDOM().getAttribute(el, 'someattr')).toEqual('someValue');