Skip to content

Commit 25f5111

Browse files
committed
feat(renderer): use EmulatedRenderer to scope component styles
1 parent a3adcca commit 25f5111

File tree

2 files changed

+92
-56
lines changed

2 files changed

+92
-56
lines changed

Diff for: nativescript-angular/renderer.ts

+89-44
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import {
22
Inject, Injectable, Optional, NgZone,
33
RendererV2, RendererFactoryV2, RendererTypeV2,
4-
// ViewEncapsulation
5-
// ɵAnimationStyles, ɵAnimationKeyframe,
4+
ViewEncapsulation,
65
} from "@angular/core";
76
import { APP_ROOT_VIEW, DEVICE } from "./platform-providers";
87
import { isBlank } from "./lang-facade";
@@ -16,8 +15,12 @@ import { escapeRegexSymbols } from "utils/utils";
1615
import { Device } from "platform";
1716

1817
// CONTENT_ATTR not exported from NativeScript_renderer - we need it for styles application.
18+
const COMPONENT_REGEX = /%COMP%/g;
1919
export const COMPONENT_VARIABLE = "%COMP%";
20+
export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
2021
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
22+
const ATTR_REPLACER = new RegExp(escapeRegexSymbols(CONTENT_ATTR), "g");
23+
const ATTR_SANITIZER = /-/g;
2124

2225
@Injectable()
2326
export class NativeScriptRendererFactory implements RendererFactoryV2 {
@@ -29,7 +32,7 @@ export class NativeScriptRendererFactory implements RendererFactoryV2 {
2932
constructor(
3033
@Optional() @Inject(APP_ROOT_VIEW) rootView: View,
3134
@Inject(DEVICE) device: Device,
32-
zone: NgZone
35+
private zone: NgZone
3336
) {
3437
this.viewUtil = new ViewUtil(device);
3538
this.setRootNgView(rootView);
@@ -50,30 +53,19 @@ export class NativeScriptRendererFactory implements RendererFactoryV2 {
5053
}
5154

5255
let renderer: NativeScriptRenderer = this.componentRenderers.get(type.id);
53-
if (isBlank(renderer)) {
54-
renderer = this.defaultRenderer;
55-
56-
let stylesLength = type.styles.length;
57-
for (let i = 0; i < stylesLength; i++) {
58-
console.log(type.styles[i]);
59-
// this.hasComponentStyles = true;
60-
let cssString = type.styles[i] + "";
61-
const realCSS = this.replaceNgAttribute(cssString, type.id);
62-
addCss(realCSS);
63-
}
64-
this.componentRenderers.set(type.id, renderer);
56+
if (!isBlank(renderer)) {
57+
return renderer;
6558
}
6659

67-
return renderer;
68-
}
69-
70-
private attrReplacer = new RegExp(escapeRegexSymbols(CONTENT_ATTR), "g");
71-
private attrSanitizer = /-/g;
72-
60+
if (type.encapsulation === ViewEncapsulation.Emulated) {
61+
renderer = new EmulatedRenderer(type, this.rootNgView, this.zone, this.viewUtil);
62+
(<EmulatedRenderer>renderer).applyToHost(element);
63+
} else {
64+
renderer = this.defaultRenderer;
65+
}
7366

74-
private replaceNgAttribute(input: string, componentId: string): string {
75-
return input.replace(this.attrReplacer,
76-
"_ng_content_" + componentId.replace(this.attrSanitizer, "_"));
67+
this.componentRenderers.set(type.id, renderer);
68+
return renderer;
7769
}
7870
}
7971

@@ -89,16 +81,21 @@ export class NativeScriptRenderer extends RendererV2 {
8981
traceLog("NativeScriptRenderer created");
9082
}
9183

92-
appendChild(parent: any, newChild: any): void {
84+
appendChild(parent: any, newChild: NgView): void {
9385
traceLog(`NativeScriptRenderer.appendChild child: ${newChild} parent: ${parent}`);
86+
console.log(typeof parent)
87+
console.log("appending child")
88+
console.log(newChild.id)
9489
this.viewUtil.insertChild(parent, newChild);
9590
}
9691

9792

98-
insertBefore(parent: any, newChild: any, refChild: any): void {
93+
insertBefore(parent: any, newChild: any, _refChild: any): void {
9994
traceLog(`NativeScriptRenderer.insertBefore child: ${newChild} parent: ${parent}`);
10095
if (parent) {
101-
parent.insertBefore(newChild, refChild);
96+
// Temporary solution until we implement nextSibling method
97+
this.appendChild(parent, newChild);
98+
// parent.insertBefore(newChild, refChild);
10299
}
103100
}
104101

@@ -112,24 +109,22 @@ export class NativeScriptRenderer extends RendererV2 {
112109
return this.rootView;
113110
}
114111

115-
parentNode(node: NgView): NgView {
116-
return node.templateParent;
112+
parentNode(node: NgView): any {
113+
return node.parent;
117114
}
118115

119116
nextSibling(_node: NgView): void {
120117
traceLog(`NativeScriptRenderer.nextSibling ${_node}`);
121118
}
122119

123120
createViewRoot(hostElement: NgView): NgView {
124-
traceLog("CREATE VIEW ROOT: " + hostElement.nodeName);
121+
traceLog(`NativeScriptRenderer.createViewRoot ${hostElement.nodeName}`)
125122
return hostElement;
126123
}
127124

128125
projectNodes(parentElement: NgView, nodes: NgView[]): void {
129126
traceLog("NativeScriptRenderer.projectNodes");
130-
nodes.forEach((node) => {
131-
this.viewUtil.insertChild(parentElement, node);
132-
});
127+
nodes.forEach((node) => this.viewUtil.insertChild(parentElement, node));
133128
}
134129

135130
destroy() {
@@ -197,17 +192,7 @@ export class NativeScriptRenderer extends RendererV2 {
197192

198193
createElement(name: any, _namespace: string): NgView {
199194
traceLog(`NativeScriptRenderer.createElement: ${name}`);
200-
201-
return this.viewUtil.createView(name, view => {
202-
console.log(view);
203-
// Set an attribute to the view to scope component-specific css.
204-
// The property name is pre-generated by Angular.
205-
206-
// if (this.hasComponentStyles) {
207-
// const cssAttribute = this.replaceNgAttribute(CONTENT_ATTR, this.componentProtoId);
208-
// view[cssAttribute] = true;
209-
// }
210-
});
195+
return this.viewUtil.createView(name)
211196
}
212197

213198
createText(_value: string): NgView {
@@ -234,3 +219,63 @@ export class NativeScriptRenderer extends RendererV2 {
234219
}
235220
}
236221

222+
class EmulatedRenderer extends NativeScriptRenderer {
223+
private contentAttr: string;
224+
private hostAttr: string;
225+
226+
constructor(
227+
private component: RendererTypeV2,
228+
rootView: NgView,
229+
zone: NgZone,
230+
viewUtil: ViewUtil,
231+
) {
232+
super(rootView, zone, viewUtil);
233+
234+
this.addStyles();
235+
this.contentAttr = shimContentAttribute(component.id);
236+
this.hostAttr = shimHostAttribute(component.id);
237+
}
238+
239+
applyToHost(view: NgView) {
240+
super.setAttribute(view, this.hostAttr, "");
241+
}
242+
243+
appendChild(parent: any, newChild: NgView): void {
244+
// Set an attribute to the view to scope component-specific css.
245+
// The property name is pre-generated by Angular.
246+
const cssAttribute = this.replaceNgAttribute(CONTENT_ATTR);
247+
newChild[cssAttribute] = true;
248+
249+
super.appendChild(parent, newChild);
250+
}
251+
252+
createElement(parent: any, name: string): NgView {
253+
const view = super.createElement(parent, name);
254+
super.setAttribute(view, this.contentAttr, "");
255+
256+
return view;
257+
}
258+
259+
private addStyles() {
260+
this.component.styles
261+
.map(s => s.toString())
262+
.map(s => this.replaceNgAttribute(s))
263+
.forEach(addCss);
264+
}
265+
266+
private replaceNgAttribute(input: string): string {
267+
return input.replace(ATTR_REPLACER , `_ng_content_${this.componentId}`);
268+
}
269+
270+
private get componentId(): string {
271+
return this.component.id.replace(ATTR_SANITIZER , "_");
272+
}
273+
}
274+
275+
function shimContentAttribute(componentShortId: string): string {
276+
return CONTENT_ATTR.replace(COMPONENT_REGEX, componentShortId);
277+
}
278+
279+
function shimHostAttribute(componentShortId: string): string {
280+
return HOST_ATTR.replace(COMPONENT_REGEX, componentShortId);
281+
}

Diff for: nativescript-angular/view-util.ts

+3-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { rendererLog as traceLog, styleError } from "./trace";
1818

1919
const IOS_PREFX: string = ":ios:";
2020
const ANDROID_PREFX: string = ":android:";
21+
const XML_ATTRIBUTES = Object.freeze([ "style", "row", "columns", "fontAttributes"]);
2122
const whiteSpaceSplitter = /\s+/;
2223

2324
export type ViewExtensions = ViewExtensions;
@@ -117,7 +118,7 @@ export class ViewUtil {
117118
}
118119
}
119120

120-
public createView(name: string, beforeAttach?: BeforeAttachAction): NgView {
121+
public createView(name: string): NgView {
121122
traceLog("Creating view:" + name);
122123

123124
if (!isKnownView(name)) {
@@ -128,10 +129,6 @@ export class ViewUtil {
128129
view.nodeName = name;
129130
view.meta = getViewMeta(name);
130131

131-
if (beforeAttach) {
132-
beforeAttach(view);
133-
}
134-
135132
return view;
136133
}
137134

@@ -144,13 +141,7 @@ export class ViewUtil {
144141
}
145142

146143
private isXMLAttribute(name: string): boolean {
147-
switch (name) {
148-
case "style": return true;
149-
case "rows": return true;
150-
case "columns": return true;
151-
case "fontAttributes": return true;
152-
default: return false;
153-
}
144+
return XML_ATTRIBUTES.indexOf(name) !== -1;
154145
}
155146

156147
private platformFilter(attribute: string): string {

0 commit comments

Comments
 (0)