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

Commit e3689be

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 426e651 commit e3689be

17 files changed

+844
-274
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

+78-63
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ 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);
2323
final COMPONENT_DIRECTIVE_INJECTOR_KEY = new Key(ComponentDirectiveInjector);
24-
final CONTENT_PORT_KEY = new Key(ContentPort);
24+
final DESTINATION_LIGHT_DOM_KEY = new Key(DestinationLightDom);
25+
final SOURCE_LIGHT_DOM_KEY = new Key(SourceLightDom);
2526
final TEMPLATE_LOADER_KEY = new Key(TemplateLoader);
2627
final SHADOW_ROOT_KEY = new Key(ShadowRoot);
2728

@@ -31,26 +32,27 @@ const int VISIBILITY_LOCAL = -1;
3132
const int VISIBILITY_DIRECT_CHILD = -2;
3233
const int VISIBILITY_CHILDREN = -3;
3334

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

5557
EventHandler eventHandler(DirectiveInjector di) => di._eventHandler;
5658

@@ -59,23 +61,24 @@ class DirectiveInjector implements DirectiveBinder {
5961
static initUID() {
6062
if (_isInit) return;
6163
_isInit = true;
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-
CONTENT_PORT_KEY.uid = CONTENT_PORT_KEY_ID;
77-
EVENT_HANDLER_KEY.uid = EVENT_HANDLER_KEY_ID;
78-
ANIMATE_KEY.uid = ANIMATE_KEY_ID;
64+
INJECTOR_KEY.uid = INJECTOR_KEY_ID;
65+
DIRECTIVE_INJECTOR_KEY.uid = DIRECTIVE_INJECTOR_KEY_ID;
66+
NODE_KEY.uid = NODE_KEY_ID;
67+
ELEMENT_KEY.uid = ELEMENT_KEY_ID;
68+
NODE_ATTRS_KEY.uid = NODE_ATTRS_KEY_ID;
69+
SCOPE_KEY.uid = SCOPE_KEY_ID;
70+
VIEW_KEY.uid = VIEW_KEY_ID;
71+
VIEW_PORT_KEY.uid = VIEW_PORT_KEY_ID;
72+
VIEW_FACTORY_KEY.uid = VIEW_FACTORY_KEY_ID;
73+
NG_ELEMENT_KEY.uid = NG_ELEMENT_KEY_ID;
74+
BOUND_VIEW_FACTORY_KEY.uid = BOUND_VIEW_FACTORY_KEY_ID;
75+
ELEMENT_PROBE_KEY.uid = ELEMENT_PROBE_KEY_ID;
76+
TEMPLATE_LOADER_KEY.uid = TEMPLATE_LOADER_KEY_ID;
77+
SHADOW_ROOT_KEY.uid = SHADOW_ROOT_KEY_ID;
78+
DESTINATION_LIGHT_DOM_KEY.uid = DESTINATION_LIGHT_DOM_KEY_ID;
79+
SOURCE_LIGHT_DOM_KEY.uid = SOURCE_LIGHT_DOM_KEY_ID;
80+
EVENT_HANDLER_KEY.uid = EVENT_HANDLER_KEY_ID;
81+
ANIMATE_KEY.uid = ANIMATE_KEY_ID;
7982
COMPONENT_DIRECTIVE_INJECTOR_KEY.uid = COMPONENT_DIRECTIVE_INJECTOR_KEY_ID;
8083
for(var i = 1; i < KEEP_ME_LAST; i++) {
8184
if (_KEYS[i].uid != i) throw 'MISSORDERED KEYS ARRAY: ${_KEYS} at $i';
@@ -98,7 +101,8 @@ class DirectiveInjector implements DirectiveBinder {
98101
, ELEMENT_PROBE_KEY
99102
, TEMPLATE_LOADER_KEY
100103
, SHADOW_ROOT_KEY
101-
, CONTENT_PORT_KEY
104+
, DESTINATION_LIGHT_DOM_KEY
105+
, SOURCE_LIGHT_DOM_KEY
102106
, EVENT_HANDLER_KEY
103107
, COMPONENT_DIRECTIVE_INJECTOR_KEY
104108
, KEEP_ME_LAST
@@ -110,6 +114,7 @@ class DirectiveInjector implements DirectiveBinder {
110114
final NodeAttrs _nodeAttrs;
111115
final Animate _animate;
112116
final EventHandler _eventHandler;
117+
LightDom lightDom;
113118
Scope scope; //TODO(misko): this should be final after we get rid of controller
114119
final View _view;
115120

@@ -282,24 +287,19 @@ class DirectiveInjector implements DirectiveBinder {
282287

283288
Object _getById(int keyId) {
284289
switch(keyId) {
285-
case INJECTOR_KEY_ID: return _appInjector;
286-
case DIRECTIVE_INJECTOR_KEY_ID: return this;
287-
case NODE_KEY_ID: return _node;
288-
case ELEMENT_KEY_ID: return _node;
289-
case NODE_ATTRS_KEY_ID: return _nodeAttrs;
290-
case ANIMATE_KEY_ID: return _animate;
291-
case SCOPE_KEY_ID: return scope;
292-
case ELEMENT_PROBE_KEY_ID: return elementProbe;
293-
case NG_ELEMENT_KEY_ID: return ngElement;
294-
case EVENT_HANDLER_KEY_ID: return _eventHandler;
295-
case CONTENT_PORT_KEY_ID:
296-
var currentInjector = _parent;
297-
while (currentInjector != null) {
298-
if (currentInjector is ComponentDirectiveInjector) return currentInjector._contentPort;
299-
currentInjector = currentInjector._parent;
300-
}
301-
return null;
302-
case VIEW_KEY_ID: return _view;
290+
case INJECTOR_KEY_ID: return _appInjector;
291+
case DIRECTIVE_INJECTOR_KEY_ID: return this;
292+
case NODE_KEY_ID: return _node;
293+
case ELEMENT_KEY_ID: return _node;
294+
case NODE_ATTRS_KEY_ID: return _nodeAttrs;
295+
case ANIMATE_KEY_ID: return _animate;
296+
case SCOPE_KEY_ID: return scope;
297+
case ELEMENT_PROBE_KEY_ID: return elementProbe;
298+
case NG_ELEMENT_KEY_ID: return ngElement;
299+
case EVENT_HANDLER_KEY_ID: return _eventHandler;
300+
case DESTINATION_LIGHT_DOM_KEY_ID: return _destLightDom;
301+
case SOURCE_LIGHT_DOM_KEY_ID: return _sourceLightDom;
302+
case VIEW_KEY_ID: return _view;
303303
default: new NoProviderError(_KEYS[keyId]);
304304
}
305305
}
@@ -367,10 +367,20 @@ class DirectiveInjector implements DirectiveBinder {
367367

368368
NgElement get ngElement {
369369
if (_ngElement == null) {
370-
_ngElement = new NgElement(_node, scope, _animate);
370+
_ngElement = new NgElement(_node, scope, _animate, _destLightDom);
371371
}
372372
return _ngElement;
373373
}
374+
375+
SourceLightDom get _sourceLightDom {
376+
var curr = _parent;
377+
while (curr != null && curr is! ComponentDirectiveInjector) {
378+
curr = curr.parent;
379+
}
380+
return curr == null || curr.parent == null ? null : curr.parent.lightDom;
381+
}
382+
383+
DestinationLightDom get _destLightDom => _parent == null ? null : _parent.lightDom;
374384
}
375385

376386
class TemplateDirectiveInjector extends DirectiveInjector {
@@ -388,29 +398,35 @@ class TemplateDirectiveInjector extends DirectiveInjector {
388398
switch(keyId) {
389399
case VIEW_FACTORY_KEY_ID: return _viewFactory;
390400
case VIEW_PORT_KEY_ID: return ((_viewPort) == null) ?
391-
_viewPort = new ViewPort(this, scope, _node, _animate) : _viewPort;
401+
_viewPort = _createViewPort() : _viewPort;
392402
case BOUND_VIEW_FACTORY_KEY_ID: return (_boundViewFactory == null) ?
393403
_boundViewFactory = _viewFactory.bind(_parent) : _boundViewFactory;
394404
default: return super._getById(keyId);
395405
}
396406
}
397407

408+
ViewPort _createViewPort() {
409+
final viewPort = new ViewPort(this, scope, _node, _animate, _destLightDom);
410+
if (_destLightDom != null) _destLightDom.addViewPort(viewPort);
411+
return viewPort;
412+
}
413+
398414
}
399415

400416
class ComponentDirectiveInjector extends DirectiveInjector {
401417

402418
final TemplateLoader _templateLoader;
403419
final ShadowRoot _shadowRoot;
404-
final ContentPort _contentPort;
405420

406421
ComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector,
407422
EventHandler eventHandler, Scope scope,
408-
this._templateLoader, this._shadowRoot, this._contentPort, [View view])
423+
this._templateLoader, this._shadowRoot, LightDom lightDom, [View view])
409424
: super(parent, appInjector, parent._node, parent._nodeAttrs, eventHandler, scope,
410425
parent._animate, view) {
411426
// A single component creates a ComponentDirectiveInjector and its DirectiveInjector parent,
412427
// so parent should never be null.
413428
assert(parent != null);
429+
_parent.lightDom = lightDom;
414430
}
415431

416432
Object _getById(int keyId) {
@@ -435,4 +451,3 @@ class ComponentDirectiveInjector extends DirectiveInjector {
435451
// For example, a local directive is visible from its component injector children.
436452
num _getDepth(int visType) => super._getDepth(visType) + 1;
437453
}
438-

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)