简体   繁体   English

如何在Angular.Dart中以编程方式添加组件?

[英]How to add a component programmatically in Angular.Dart?

I would like to dynamically build a component tree basing on some information received from AJAX calls. 我想基于从AJAX调用接收的一些信息动态构建组件树。

How to programmatically add a component to the DOM from inside of other component? 如何以编程方式从其他组件内部向DOM添加组件? I have <outer-comp> and I would like, basing on some logic, insert an <inner-comp> . 我有<outer-comp> ,我希望,基于一些逻辑,插入一个<inner-comp> The following code just inserts the elements <inner-comp></inner-comp> to the DOM, and not actual <inner-comp> representation. 以下代码只是将元素<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>");
  }
}

Update: I managed to render the inner component correctly in the following way, but I'm still not sure if this is the proper way: 更新:我设法以下列方式正确渲染内部组件,但我仍然不确定这是否正确:

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

} }

The API has changed in AngularDart 0.9.9: AngularDart 0.9.9中的API已更改:

  • BlockFactory now is ViewFactory BlockFactory现在是ViewFactory
  • scope.$new now seems to be scope.createChild(scope.context) 范围。$ new现在似乎是scope.createChild(scope.context)
  • injector.createChild(modules) now requires a list of modules (instead of a single one) injector.createChild(modules)现在需要一个模块列表(而不是一个模块)

AngularDart 0.10.0 introduces these changes: AngularDart 0.10.0引入了以下更改:

  • NgShadowRootAware not is ShadowRootAware NgShadowRootAware不是ShadowRootAware
  • ..value() now is ..bind(., toValue: .) ..value()现在是..bind(。,toValue:。)

So the code of pavelgj now looks like so: 所以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]);
    }
  }

This would be a proper use of the block API. 这将是块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 . 此外,如果您需要重新编译内部模板,请确保在之前的childScope上执行childScope.$destroy()

The above code samples on longer work because of changes in the Angular Dart library. 由于Angular Dart库中的更改,上面的代码会对较长时间的工作进行采样。 Specifically ViewFactory.call which no longer takes an injector but takes a Scope and a DirectiveInjector. 特别是ViewFactory.call,它不再使用注入器,而是使用Scope和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. 组件显示但没有任何绑定被替换(例如,我看到{{cmp.value}}。

Here's the code I'm using. 这是我正在使用的代码。 I think the issue here is that DirectiveInjector is coming in as null. 我认为这里的问题是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);
  }
}

I did finally get this to work but was not happy with having to add a timer: 我终于让它工作但不满意不得不添加一个计时器:

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

EDIT 编辑

The package http://pub.dartlang.org/packages/bwu_angular contains this decorator/directive as bwu-safe-html http://pub.dartlang.org/packages/bwu_angular包含这个装饰/指令为bwu-safe-html

------ ------

I use a custom directive for that 我使用自定义指令

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

It can be used like 它可以像

my-bind-html='ctrl.somehtml'

Angular issue 角度问题
I created an issue to include this functionality into Angulars ng-bind-html https://github.com/angular/angular.dart/issues/742 (declined) 我创建了一个问题,将此功能包含在Angulars ng-bind-html https://github.com/angular/angular.dart/issues/742 (已拒绝)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM