¿Cómo agregar un componente programáticamente en Angular.Dart?
angular-dart shadow-dom (5)
Este sería un uso adecuado del bloque API.
class AppComponent extends NgShadowRootAware {
Compiler compiler;
Injector injector;
Scope scope;
DirectiveMap directives;
AppComponent(this.compiler, this.injector, this.scope, this.directives);
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
BlockFactory template = compiler([inner], directives);
Scope childScope = scope.$new();
Injector childInjector =
injector.createChild(new Module()..value(Scope, childScope));
template(childInjector, [inner]);
}
}
Además, si alguna vez necesita volver a compilar la plantilla interna, asegúrese de hacer childScope.$destroy()
en el childScope
anterior.
Me gustaría construir dinámicamente un árbol de componentes basándose en alguna información recibida de las llamadas AJAX.
¿Cómo agregar un componente al DOM mediante programación desde dentro de otro componente? Tengo <outer-comp>
y me gustaría, basándome en alguna lógica, insertar un <inner-comp>
. El siguiente código solo inserta los elementos <inner-comp></inner-comp>
en el DOM, y no la representación real <inner-comp>
.
@NgComponent(
selector: ''outer-comp'',
templateUrl: ''view/outer_component.html'',
cssUrl: ''view/outer_component.css'',
publishAs: ''outer''
)
class AppComponent extends NgShadowRootAware {
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
}
}
Actualización: Logré procesar correctamente el componente interno de la siguiente manera, pero aún no estoy seguro de que sea la correcta:
class AppComponent extends NgShadowRootAware {
Compiler compiler;
Injector injector;
AppComponent(this.compiler, this.injector);
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
BlockFactory template = compiler(inner.nodes);
var block = template(injector);
inner.replaceWith(block.elements[0]);
}
}
Finalmente conseguí que esto funcionara, pero no estaba contento con tener que agregar un temporizador:
@Injectable()
class AppComponent{
NodeValidator validator;
Compiler _compiler;
DirectiveInjector _directiveInjector;
DirectiveMap _directiveMap;
NodeTreeSanitizer _nodeTreeSanitizer;
Injector _appInjector;
Scope _scope;
AppComponent(this._directiveInjector, this._compiler, this._directiveMap, this._nodeTreeSanitizer, this._appInjector, this._scope) {
validator = new MyValidator();
}
void addElement(String id, String elementHTML) {
DivElement container = querySelector(id);
DivElement inner = new DivElement();
container.append(inner);
Element element = new Element.html(elementHTML, validator: validator);
ViewFactory viewFactory = _compiler.call([element], _directiveMap);
if (_scope != null) {
Scope childScope = _scope.createProtoChild();
View newView = viewFactory.call(childScope, _directiveInjector);
newView.nodes.forEach((node) => inner.append(node));
Timer.run(() => childScope.apply());
} else {
print("scope is null");
}
}
}
La API ha cambiado en AngularDart 0.9.9:
- BlockFactory ahora es ViewFactory
- scope. $ new ahora parece ser scope.createChild (scope.context)
- injector.createChild (módulos) ahora requiere una lista de módulos (en lugar de uno solo)
AngularDart 0.10.0 introduce estos cambios:
- NgShadowRootAware no es ShadowRootAware
- ..value () ahora es ..bind (., toValue:.)
Así que el código de pavelgj ahora se ve así:
class AppComponent extends ShadowRootAware {
Compiler compiler;
Injector injector;
Scope scope;
DirectiveMap directives;
AppComponent(this.compiler, this.injector, this.scope, this.directives);
void onShadowRoot(ShadowRoot shadowRoot) {
DivElement inner = shadowRoot.querySelector("#inner");
inner.appendHtml("<inner-comp></inner-comp>");
ViewFactory template = compiler([inner], directives);
Scope childScope = scope.createChild(scope.context);
Injector childInjector =
injector.createChild([new Module()..bind(Scope, toValue: childScope)]);
template(childInjector, [inner]);
}
}
Los ejemplos de código anteriores en trabajos más largos debido a cambios en la biblioteca de dardos angulares. Específicamente ViewFactory.call que ya no toma un inyector sino que toma un Alcance y un Inyector Directivo. He intentado adaptar lo que está arriba y me acerco mucho. El componente aparece pero ninguno de los enlaces se reemplaza (veo {{cmp.value}} por ejemplo.
Aquí está el código que estoy usando. Creo que el problema aquí es que Directurejector está entrando como nulo.
void main() {
IBMModule module = new IBMModule();
AngularModule angularModule = new AngularModule();
Injector injector = applicationFactory()
.addModule(module)
.run();
AppComponent appComponent = injector.get(AppComponent);
appComponent.addElement("<brazos-input-string label=''test''/>");
}
@Injectable()
class AppComponent {
NodeValidator validator;
Compiler _compiler;
DirectiveInjector _injector;
DirectiveMap _directiveMap;
NodeTreeSanitizer _nodeTreeSanitizer;
Scope _scope;
AppComponent(this._injector, this._compiler, this._directiveMap, this._scope, this._nodeTreeSanitizer) {
validator = new NodeValidatorBuilder.common()
..allowCustomElement("BRAZOS-INPUT-STRING")
..allowHtml5()
..allowTemplating();
}
void addElement(String elementHTML) {
DivElement container = querySelector("#container");
DivElement inner = new DivElement();
inner.setInnerHtml(elementHTML, validator: validator);
ViewFactory viewFactory = _compiler.call([inner], _directiveMap);
Scope childScope = _scope.createChild(new PrototypeMap(_scope.context));
if (_injector == null) {
print("injector is null");
}
View newView = viewFactory.call(childScope, _injector);
container.append(inner);
newView.nodes.forEach((node) => inner.append(node));
}
}
class IBMModule extends Module {
IBMModule() {
bind(BrazosInputStringComponent);
bind(BrazosTextAreaComponent);
bind(BrazosButtonComponent);
bind(ProcessDataProvider, toImplementation: ActivitiDataProvider);
bind(AppComponent);
}
}
EDITAR
El paquete http://pub.dartlang.org/packages/bwu_angular contiene este decorador / directiva como bwu-safe-html
------
Yo uso una directiva personalizada para eso
@NgDirective(
selector: ''[my-bind-html]''
)
class MyBindHtmlDirective {
static dom.NodeValidator validator;
dom.Element _element;
Compiler _compiler;
Injector _injector;
DirectiveMap _directiveMap;
MyBindHtmlDirective(this._element, this._injector, this._compiler, this._directiveMap) {
validator = new dom.NodeValidatorBuilder.common()
..allowHtml5()
..allowImages();
}
@NgOneWay(''my-bind-html'')
set value(value) {
if(value == null) {
_element.nodes.clear();
return;
}
_element.setInnerHtml((value == null ? '''' : value.toString()),
validator: validator);
if(value != null) {
_compiler(_element.childNodes, _directiveMap)(_injector, _element.childNodes);
}
}
}
Se puede usar como
my-bind-html=''ctrl.somehtml''
Problema angular
Creé un problema para incluir esta funcionalidad en Angulars ng-bind-html
https://github.com/angular/angular.dart/issues/742 (rechazado)