Как добавить компонент программно в Angular.Дарт?
Я хотел бы динамически построить дерево компонентов на основе некоторой информации, полученной от вызовов AJAX.
как программно добавить компонент в DOM изнутри другого компонента? У меня <outer-comp>
и я хотел бы, основываясь на некоторой логике, вставить <inner-comp>
. Следующий код просто вставляет элементы <inner-comp></inner-comp>
к DOM, а не фактический <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>");
}
}
обновление: Мне удалось отобразить внутренний компонент правильно следующим образом, но я все еще не уверен, что это правильный способ:
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]);
}
}
5 ответов
Это было бы правильное использование 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]);
}
}
кроме того, если вам когда-либо понадобится перекомпилировать внутренний шаблон, убедитесь, что вы делаете childScope.$destroy()
на .
API изменился в AngularDart 0.9.9:
- BlockFactory теперь ViewFactory
- объем.$new теперь кажется областью применения.createChild (scope.контекст)
- инжектор.createChild (modules) теперь требует список модулей (вместо одного)
AngularDart 0.10.0 вводит следующие изменения:
- NgShadowRootAware не является ShadowRootAware
- ..value () now is ..связывать.(, toValue: .)
Итак, код pavelgj теперь выглядит так:
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]);
}
}
приведенные выше примеры кода дольше работают из-за изменений в библиотеке угловых дротиков. В Частности, ViewFactory.вызов, который больше не принимает инжектор, но принимает область и DirectiveInjector. Я пытался приспособиться к тому, что наверху, и подобрался очень близко. Компонент отображается, но ни одна из Привязок не заменяется (я вижу {{cmp.значения}}, например.
вот код, который я использую. Я думаю, что проблема здесь в том, что DirectiveInjector входит как 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);
}
}
Я, наконец, получил это, чтобы работать, но не был доволен тем, чтобы добавить таймер:
@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");
}
}
}
редактировать
пакета http://pub.dartlang.org/packages/bwu_angular содержится этот декоратор/директивы bwu-safe-html
------
Я использую пользовательскую директиву для этого
@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);
}
}
}
его можно использовать как
my-bind-html='ctrl.somehtml'
Угловое вопрос
Я создал проблему, чтобы включить эту функциональность в Angulars ng-bind-html
https://github.com/angular/angular.dart/issues/742 (отклонено)