【问题标题】:How to add a component programmatically in Angular.Dart?如何在 Angular.Dart 中以编程方式添加组件?
【发布时间】:2013-12-23 18:49:49
【问题描述】:

我想根据从 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]); 
  }

}

【问题讨论】:

  • 你能展示你如何调用你的 AppComponent 构造函数吗?你如何创建一个新的编译器和一个新的注入器?
  • @0xor1 不需要调用构造函数——这就是依赖注入的作用;-)
  • 那么你将如何在不在 NgComponent 类中的代码中实现这一点?说在你想调用document.body.appendHtml('&lt;my-comp&gt;&lt;/my-comp&gt;')的主要方法中,你将如何获得角度来编译它?

标签: dart angular-dart shadow-dom


【解决方案1】:

我终于让它工作了,但对不得不添加一个计时器并不满意:

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

【讨论】:

  • 我认为new Future(() =&gt; childScope.apply()); 会稍微好一点,或者scheduleMicroTask(() =&gt; childScope.apply());(虽然可能不起作用)。
  • @GünterZöchbauer 我相信正确的方法是将其安排为微任务。为什么你认为这行不通?
  • ScheduleMicrotask 可能执行得太早了。
【解决方案2】:

由于 Angular Dart 库的更改,上述代码示例的工作时间更长。特别是 ViewFactory.call,它不再需要一个注入器,而是一个 Scope 和一个 DirectiveInjector。我已经尝试过调整上面的内容,并且非常接近。组件显示,但没有任何绑定被替换(例如,我看到 {{cmp.value}}。

这是我正在使用的代码。我认为这里的问题是 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);
  }
}

【讨论】:

    【解决方案3】:

    编辑

    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(已拒绝)

    【讨论】:

    • 问题已关闭。出于安全原因,此功能不会添加到 Angular 中。
    【解决方案4】:

    AngularDart 0.9.9 中的 API 发生了变化:

    • BlockFactory 现在是 ViewFactory
    • scope.$new 现在似乎是 scope.createChild(scope.context)
    • injector.createChild(modules) 现在需要一个模块列表(而不是一个)

    AngularDart 0.10.0 引入了这些变化:

    • NgShadowRootAware 不是 ShadowRootAware
    • ..value() 现在是 ..bind(., 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]);
        }
      }
    

    【讨论】:

    • 我想你只是不小心复制粘贴了@pavelgj 答案。
    • 哎哟!你说得对。希望我设法修复了代码 - 似乎我再次摆脱了我的测试版本。无论如何,ng-include 做了类似的事情,所以看看NgIncludeDirective 的实现总是一个好主意。
    • ng-repeat 怎么样?当我尝试附加诸如“”之类的内容时,NgRepeat 指令不起作用。甚至不会调用 NgRepeat 的构造函数。过了一会儿,我开始怀疑这不是我的错。您认为 ng-repeat 应该起作用吗,还是 Pavel 的示例中缺少重要的初始化?
    【解决方案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 上执行 childScope.$destroy()

    【讨论】:

    • 感谢您的快速响应,您的解决方案运行良好。
    • @pavelgi:此代码在 Angular 0.9.4 中运行良好。但在 Angular 0.9.6 中,compiler(...) 需要第二个参数(DirectiveMap 指令)。什么是 DirectiveMap,我应该将什么传递给函数?
    • @pavelgj:请您更新您的回复以匹配 Angular 0.9.9 中引入的更改?
    • 如何为 AppComponent 构造函数添加参数?
    • 您只需声明您需要的任何参数 - 依赖注入机制负责为您提供正确的值。
    猜你喜欢
    • 1970-01-01
    • 2021-05-16
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 2019-01-25
    • 2018-11-02
    • 2015-03-11
    • 2014-07-21
    相关资源
    最近更新 更多