Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit ffdab53

Browse files
committed
feat(components): add support for multiple insertion points for transcluding components
Add implementations of the light dom and the content tag to support multiple insertion points for transcluding components.
1 parent aa1fcfd commit ffdab53

17 files changed

+847
-276
lines changed

lib/core_dom/content_tag.dart

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
part of angular.core.dom_internal;
2+
3+
abstract class _ContentStrategy {
4+
void attach();
5+
void detach();
6+
void insert(Iterable<dom.Node> nodes);
7+
}
8+
9+
/**
10+
* A null implementation of the content tag that is used by Shadow DOM components.
11+
* The distribution is handled by the browser, so Angular does nothing.
12+
*/
13+
class _ShadowDomContent implements _ContentStrategy {
14+
void attach(){}
15+
void detach(){}
16+
void insert(Iterable<dom.Node> nodes){}
17+
}
18+
19+
/**
20+
* An implementation of the content tag that is used by transcluding components.
21+
* It is used when the content tag is not a direct child of another component,
22+
* and thus does not affect redistribution.
23+
*/
24+
class _RenderedTranscludingContent implements _ContentStrategy {
25+
final SourceLightDom _sourceLightDom;
26+
final Content _content;
27+
28+
static final dom.ScriptElement _beginScriptTemplate =
29+
new dom.ScriptElement()..type = "ng/content";
30+
31+
static final dom.ScriptElement _endScriptTemplate =
32+
new dom.ScriptElement()..type = "ng/content";
33+
34+
dom.ScriptElement _beginScript;
35+
dom.ScriptElement _endScript;
36+
37+
_RenderedTranscludingContent(this._content, this._sourceLightDom);
38+
39+
void attach(){
40+
_replaceContentElementWithScriptTags();
41+
_sourceLightDom.redistribute();
42+
}
43+
44+
void detach(){
45+
_removeScriptTags();
46+
_sourceLightDom.redistribute();
47+
}
48+
49+
void insert(Iterable<dom.Node> nodes){
50+
final p = _endScript.parent;
51+
if (p != null) p.insertAllBefore(nodes, _endScript);
52+
}
53+
54+
void _replaceContentElementWithScriptTags() {
55+
_beginScript = _beginScriptTemplate.clone(true);
56+
_endScript = _endScriptTemplate.clone(true);
57+
58+
final el = _content.element;
59+
el.parent.insertBefore(_beginScript, el);
60+
el.parent.insertBefore(_endScript, el);
61+
el.remove();
62+
}
63+
64+
void _removeScriptTags() {
65+
_removeNodesBetweenScriptTags();
66+
_beginScript.remove();
67+
_endScript.remove();
68+
}
69+
70+
void _removeNodesBetweenScriptTags() {
71+
final p = _beginScript.parent;
72+
for (var next = _beginScript.nextNode;
73+
next.nodeType != dom.Node.ELEMENT_NODE || next.attributes["ng/content"] != null;
74+
next = _beginScript.nextNode) {
75+
p.nodes.remove(next);
76+
}
77+
}
78+
}
79+
80+
/**
81+
* An implementation of the content tag that is used by transcluding components.
82+
* It is used when the content tag is a direct child of another component,
83+
* and thus does not get rendered but only affect the distribution of its parent component.
84+
*/
85+
class _IntermediateTranscludingContent implements _ContentStrategy {
86+
final SourceLightDom _sourceLightDom;
87+
final DestinationLightDom _destinationLightDom;
88+
final Content _content;
89+
90+
_IntermediateTranscludingContent(this._content, this._sourceLightDom, this._destinationLightDom);
91+
92+
void attach(){
93+
_sourceLightDom.redistribute();
94+
}
95+
96+
void detach(){
97+
_sourceLightDom.redistribute();
98+
}
99+
100+
void insert(Iterable<dom.Node> nodes){
101+
_content.element.nodes = nodes;
102+
_destinationLightDom.redistribute();
103+
}
104+
}
105+
106+
@Decorator(selector: 'content')
107+
class Content implements AttachAware, DetachAware {
108+
dom.Element element;
109+
110+
@NgAttr('select')
111+
String select;
112+
113+
final SourceLightDom _sourceLightDom;
114+
final DestinationLightDom _destinationLightDom;
115+
var _strategy;
116+
117+
Content(this.element, this._sourceLightDom, this._destinationLightDom, View view) {
118+
view.addContent(this);
119+
}
120+
121+
void attach() => strategy.attach();
122+
void detach() => strategy.detach();
123+
void insert(Iterable<dom.Node> nodes) => strategy.insert(nodes);
124+
125+
_ContentStrategy get strategy {
126+
if (_strategy == null) _strategy = _createContentStrategy();
127+
return _strategy;
128+
}
129+
130+
_ContentStrategy _createContentStrategy() {
131+
if (_sourceLightDom == null) {
132+
return new _ShadowDomContent();
133+
} else if (_destinationLightDom != null && _destinationLightDom.hasRoot(element)) {
134+
return new _IntermediateTranscludingContent(this, _sourceLightDom, _destinationLightDom);
135+
} else {
136+
return new _RenderedTranscludingContent(this, _sourceLightDom);
137+
}
138+
}
139+
}

lib/core_dom/directive_injector.dart

+82-65
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ import 'package:angular/core/module.dart' show Scope, RootScope;
1414
import 'package:angular/core/annotation.dart' show Visibility, DirectiveBinder;
1515
import 'package:angular/core_dom/module_internal.dart'
1616
show Animate, View, ViewFactory, BoundViewFactory, ViewPort, NodeAttrs, ElementProbe,
17-
NgElement, ContentPort, TemplateLoader, ShadowRootEventHandler, EventHandler;
17+
NgElement, DestinationLightDom, SourceLightDom, LightDom, TemplateLoader, ShadowRootEventHandler, EventHandler;
1818

1919
var _TAG_GET = new UserTag('DirectiveInjector.get()');
2020
var _TAG_INSTANTIATE = new UserTag('DirectiveInjector.instantiate()');
2121

2222
final DIRECTIVE_INJECTOR_KEY = new Key(DirectiveInjector);
23-
final CONTENT_PORT_KEY = new Key(ContentPort);
23+
final DESTINATION_LIGHT_DOM_KEY = new Key(DestinationLightDom);
24+
final SOURCE_LIGHT_DOM_KEY = new Key(SourceLightDom);
2425
final TEMPLATE_LOADER_KEY = new Key(TemplateLoader);
2526
final SHADOW_ROOT_KEY = new Key(ShadowRoot);
2627

@@ -30,25 +31,26 @@ const int VISIBILITY_LOCAL = -1;
3031
const int VISIBILITY_DIRECT_CHILD = -2;
3132
const int VISIBILITY_CHILDREN = -3;
3233

33-
const int UNDEFINED_ID = 0;
34-
const int INJECTOR_KEY_ID = 1;
35-
const int DIRECTIVE_INJECTOR_KEY_ID = 2;
36-
const int NODE_KEY_ID = 3;
37-
const int ELEMENT_KEY_ID = 4;
38-
const int NODE_ATTRS_KEY_ID = 5;
39-
const int ANIMATE_KEY_ID = 6;
40-
const int SCOPE_KEY_ID = 7;
41-
const int VIEW_KEY_ID = 8;
42-
const int VIEW_PORT_KEY_ID = 9;
43-
const int VIEW_FACTORY_KEY_ID = 10;
44-
const int NG_ELEMENT_KEY_ID = 11;
45-
const int BOUND_VIEW_FACTORY_KEY_ID = 12;
46-
const int ELEMENT_PROBE_KEY_ID = 13;
47-
const int TEMPLATE_LOADER_KEY_ID = 14;
48-
const int SHADOW_ROOT_KEY_ID = 15;
49-
const int CONTENT_PORT_KEY_ID = 16;
50-
const int EVENT_HANDLER_KEY_ID = 17;
51-
const int KEEP_ME_LAST = 18;
34+
const int UNDEFINED_ID = 0;
35+
const int INJECTOR_KEY_ID = 1;
36+
const int DIRECTIVE_INJECTOR_KEY_ID = 2;
37+
const int NODE_KEY_ID = 3;
38+
const int ELEMENT_KEY_ID = 4;
39+
const int NODE_ATTRS_KEY_ID = 5;
40+
const int ANIMATE_KEY_ID = 6;
41+
const int SCOPE_KEY_ID = 7;
42+
const int VIEW_KEY_ID = 8;
43+
const int VIEW_PORT_KEY_ID = 9;
44+
const int VIEW_FACTORY_KEY_ID = 10;
45+
const int NG_ELEMENT_KEY_ID = 11;
46+
const int BOUND_VIEW_FACTORY_KEY_ID = 12;
47+
const int ELEMENT_PROBE_KEY_ID = 13;
48+
const int TEMPLATE_LOADER_KEY_ID = 14;
49+
const int SHADOW_ROOT_KEY_ID = 15;
50+
const int DESTINATION_LIGHT_DOM_KEY_ID = 16;
51+
const int SOURCE_LIGHT_DOM_KEY_ID = 17;
52+
const int EVENT_HANDLER_KEY_ID = 18;
53+
const int KEEP_ME_LAST = 19;
5254

5355
EventHandler eventHandler(DirectiveInjector di) => di._eventHandler;
5456

@@ -57,23 +59,24 @@ class DirectiveInjector implements DirectiveBinder {
5759
static initUID() {
5860
if (_isInit) return;
5961
_isInit = true;
60-
INJECTOR_KEY.uid = INJECTOR_KEY_ID;
61-
DIRECTIVE_INJECTOR_KEY.uid = DIRECTIVE_INJECTOR_KEY_ID;
62-
NODE_KEY.uid = NODE_KEY_ID;
63-
ELEMENT_KEY.uid = ELEMENT_KEY_ID;
64-
NODE_ATTRS_KEY.uid = NODE_ATTRS_KEY_ID;
65-
SCOPE_KEY.uid = SCOPE_KEY_ID;
66-
VIEW_KEY.uid = VIEW_KEY_ID;
67-
VIEW_PORT_KEY.uid = VIEW_PORT_KEY_ID;
68-
VIEW_FACTORY_KEY.uid = VIEW_FACTORY_KEY_ID;
69-
NG_ELEMENT_KEY.uid = NG_ELEMENT_KEY_ID;
70-
BOUND_VIEW_FACTORY_KEY.uid = BOUND_VIEW_FACTORY_KEY_ID;
71-
ELEMENT_PROBE_KEY.uid = ELEMENT_PROBE_KEY_ID;
72-
TEMPLATE_LOADER_KEY.uid = TEMPLATE_LOADER_KEY_ID;
73-
SHADOW_ROOT_KEY.uid = SHADOW_ROOT_KEY_ID;
74-
CONTENT_PORT_KEY.uid = CONTENT_PORT_KEY_ID;
75-
EVENT_HANDLER_KEY.uid = EVENT_HANDLER_KEY_ID;
76-
ANIMATE_KEY.uid = ANIMATE_KEY_ID;
62+
INJECTOR_KEY.uid = INJECTOR_KEY_ID;
63+
DIRECTIVE_INJECTOR_KEY.uid = DIRECTIVE_INJECTOR_KEY_ID;
64+
NODE_KEY.uid = NODE_KEY_ID;
65+
ELEMENT_KEY.uid = ELEMENT_KEY_ID;
66+
NODE_ATTRS_KEY.uid = NODE_ATTRS_KEY_ID;
67+
SCOPE_KEY.uid = SCOPE_KEY_ID;
68+
VIEW_KEY.uid = VIEW_KEY_ID;
69+
VIEW_PORT_KEY.uid = VIEW_PORT_KEY_ID;
70+
VIEW_FACTORY_KEY.uid = VIEW_FACTORY_KEY_ID;
71+
NG_ELEMENT_KEY.uid = NG_ELEMENT_KEY_ID;
72+
BOUND_VIEW_FACTORY_KEY.uid = BOUND_VIEW_FACTORY_KEY_ID;
73+
ELEMENT_PROBE_KEY.uid = ELEMENT_PROBE_KEY_ID;
74+
TEMPLATE_LOADER_KEY.uid = TEMPLATE_LOADER_KEY_ID;
75+
SHADOW_ROOT_KEY.uid = SHADOW_ROOT_KEY_ID;
76+
DESTINATION_LIGHT_DOM_KEY.uid = DESTINATION_LIGHT_DOM_KEY_ID;
77+
SOURCE_LIGHT_DOM_KEY.uid = SOURCE_LIGHT_DOM_KEY_ID;
78+
EVENT_HANDLER_KEY.uid = EVENT_HANDLER_KEY_ID;
79+
ANIMATE_KEY.uid = ANIMATE_KEY_ID;
7780
for(var i = 1; i < KEEP_ME_LAST; i++) {
7881
if (_KEYS[i].uid != i) throw 'MISSORDERED KEYS ARRAY: ${_KEYS} at $i';
7982
}
@@ -95,7 +98,8 @@ class DirectiveInjector implements DirectiveBinder {
9598
, ELEMENT_PROBE_KEY
9699
, TEMPLATE_LOADER_KEY
97100
, SHADOW_ROOT_KEY
98-
, CONTENT_PORT_KEY
101+
, DESTINATION_LIGHT_DOM_KEY
102+
, SOURCE_LIGHT_DOM_KEY
99103
, EVENT_HANDLER_KEY
100104
, KEEP_ME_LAST
101105
];
@@ -106,6 +110,7 @@ class DirectiveInjector implements DirectiveBinder {
106110
final NodeAttrs _nodeAttrs;
107111
final Animate _animate;
108112
final EventHandler _eventHandler;
113+
LightDom lightDom;
109114
Scope scope; //TODO(misko): this should be final after we get rid of controller
110115
final View _view;
111116

@@ -276,24 +281,19 @@ class DirectiveInjector implements DirectiveBinder {
276281

277282
Object _getById(int keyId) {
278283
switch(keyId) {
279-
case INJECTOR_KEY_ID: return _appInjector;
280-
case DIRECTIVE_INJECTOR_KEY_ID: return this;
281-
case NODE_KEY_ID: return _node;
282-
case ELEMENT_KEY_ID: return _node;
283-
case NODE_ATTRS_KEY_ID: return _nodeAttrs;
284-
case ANIMATE_KEY_ID: return _animate;
285-
case SCOPE_KEY_ID: return scope;
286-
case ELEMENT_PROBE_KEY_ID: return elementProbe;
287-
case NG_ELEMENT_KEY_ID: return ngElement;
288-
case EVENT_HANDLER_KEY_ID: return _eventHandler;
289-
case CONTENT_PORT_KEY_ID:
290-
var currentInjector = _parent;
291-
while (currentInjector != null) {
292-
if (currentInjector is ComponentDirectiveInjector) return currentInjector._contentPort;
293-
currentInjector = currentInjector._parent;
294-
}
295-
return null;
296-
case VIEW_KEY_ID: return _view;
284+
case INJECTOR_KEY_ID: return _appInjector;
285+
case DIRECTIVE_INJECTOR_KEY_ID: return this;
286+
case NODE_KEY_ID: return _node;
287+
case ELEMENT_KEY_ID: return _node;
288+
case NODE_ATTRS_KEY_ID: return _nodeAttrs;
289+
case ANIMATE_KEY_ID: return _animate;
290+
case SCOPE_KEY_ID: return scope;
291+
case ELEMENT_PROBE_KEY_ID: return elementProbe;
292+
case NG_ELEMENT_KEY_ID: return ngElement;
293+
case EVENT_HANDLER_KEY_ID: return _eventHandler;
294+
case DESTINATION_LIGHT_DOM_KEY_ID: return _destLightDom;
295+
case SOURCE_LIGHT_DOM_KEY_ID: return _sourceLightDom;
296+
case VIEW_KEY_ID: return _view;
297297
default: new NoProviderError(_KEYS[keyId]);
298298
}
299299
}
@@ -361,10 +361,20 @@ class DirectiveInjector implements DirectiveBinder {
361361

362362
NgElement get ngElement {
363363
if (_ngElement == null) {
364-
_ngElement = new NgElement(_node, scope, _animate);
364+
_ngElement = new NgElement(_node, scope, _animate, _destLightDom);
365365
}
366366
return _ngElement;
367367
}
368+
369+
SourceLightDom get _sourceLightDom {
370+
var curr = _parent;
371+
while (curr != null && curr is! ComponentDirectiveInjector) {
372+
curr = curr.parent;
373+
}
374+
return curr == null || curr.parent == null ? null : curr.parent.lightDom;
375+
}
376+
377+
DestinationLightDom get _destLightDom => _parent == null ? null : _parent.lightDom;
368378
}
369379

370380
class TemplateDirectiveInjector extends DirectiveInjector {
@@ -382,26 +392,34 @@ class TemplateDirectiveInjector extends DirectiveInjector {
382392
switch(keyId) {
383393
case VIEW_FACTORY_KEY_ID: return _viewFactory;
384394
case VIEW_PORT_KEY_ID: return ((_viewPort) == null) ?
385-
_viewPort = new ViewPort(this, scope, _node, _animate) : _viewPort;
395+
_viewPort = _createViewPort() : _viewPort;
386396
case BOUND_VIEW_FACTORY_KEY_ID: return (_boundViewFactory == null) ?
387397
_boundViewFactory = _viewFactory.bind(_parent) : _boundViewFactory;
388398
default: return super._getById(keyId);
389399
}
390400
}
391401

402+
ViewPort _createViewPort() {
403+
final view = getByKey(VIEW_KEY);
404+
final viewPort = new ViewPort(this, scope, view, _node, _animate, _destLightDom);
405+
if (_destLightDom != null) _destLightDom.addViewPort(viewPort);
406+
return viewPort;
407+
}
408+
392409
}
393410

394411
class ComponentDirectiveInjector extends DirectiveInjector {
395412

396413
final TemplateLoader _templateLoader;
397414
final ShadowRoot _shadowRoot;
398-
final ContentPort _contentPort;
399415

400416
ComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector,
401-
EventHandler eventHandler, Scope scope, View view,
402-
this._templateLoader, this._shadowRoot, this._contentPort)
403-
: super(parent, appInjector, parent._node, parent._nodeAttrs, eventHandler, scope, view,
404-
parent._animate);
417+
EventHandler eventHandler, Scope scope, View view,
418+
this._templateLoader, this._shadowRoot, LightDom lightDom)
419+
: super(parent, appInjector, parent._node, parent._nodeAttrs, eventHandler, scope, view,
420+
parent._animate) {
421+
_parent.lightDom = lightDom;
422+
}
405423

406424
Object _getById(int keyId) {
407425
switch(keyId) {
@@ -423,4 +441,3 @@ class ComponentDirectiveInjector extends DirectiveInjector {
423441
// For example, a local directive is visible from its component injector children.
424442
num _getDepth(int visType) => super._getDepth(visType) + 1;
425443
}
426-

lib/core_dom/shadowless_shadow_root.dart renamed to lib/core_dom/emulated_shadow_root.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
part of angular.core.dom_internal;
2-
class ShadowlessShadowRoot implements dom.ShadowRoot {
2+
class EmulatedShadowRoot implements dom.ShadowRoot {
33
dom.Element _element;
4-
ShadowlessShadowRoot(this._element);
4+
EmulatedShadowRoot(this._element);
55
_notSupported() { throw new UnsupportedError("Not supported"); }
66
dom.Element get activeElement => _notSupported();
77
dom.Element get host => _notSupported();

0 commit comments

Comments
 (0)