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);
}
}