How to add a component programmatically in Angular.Dart?
The API has changed in AngularDart 0.9.9:
- BlockFactory now is ViewFactory
- scope.$new now seems to be scope.createChild(scope.context)
- injector.createChild(modules) now requires a list of modules (instead of a single one)
AngularDart 0.10.0 introduces these changes:
- NgShadowRootAware not is ShadowRootAware
- ..value() now is ..bind(., toValue: .)
So the code of pavelgj now looks like so:
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]); } }
This would be a proper use of the block 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]); }}
Also, if you ever need to recompile the inner template make sure you do childScope.$destroy()
on the previous childScope
.
The above code samples on longer work because of changes in the Angular Dart library. Specifically ViewFactory.call which no longer takes an injector but takes a Scope and a DirectiveInjector. I've tried adapting what's above and I get very close. The component shows up but none of the bindings are replaced (I see {{cmp.value}} for example.
Here's the code I'm using. I think the issue here is that DirectiveInjector is coming in as null.
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); }}