4

I'm trying to create an angular dart component dynamically. I know it's not a best practice but I have to because of how my angular widgets are being inserted.

I based my work off of:

How to add a component programatically in Angular.Dart?

The code samples on longer work because of changes in the Angular Dart library.

I got this code to work but it's inconsistent. The solution was the Timer.run() to fire the scope.apply. The problem with that is:

  1. It stinks to make a call like that and would perform terribly with lots of components
  2. It seems to work randomly. Most of the time it does but occasionally it doesn't do the {{foo}} replacements
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'/>");
}

class MyValidator implements NodeValidator {


  bool allowsElement(Element element) {
    return true;
  }

  bool allowsAttribute(Element element, String attributeName, String value) {
    return true;
  }

}

@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 elementHTML) {
    DivElement container = querySelector("#container");
    DivElement inner = new DivElement();
    container.append(inner);
    Element element = new Element.html(elementHTML, validator: validator);
    // inner.setInnerHtml(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");
    }
  }
}


class IBMModule extends Module {
  IBMModule() {
    bind(BrazosInputStringComponent);
    bind(BrazosTextAreaComponent);
    bind(BrazosButtonComponent);
    bind(ProcessDataProvider, toImplementation: ActivitiDataProvider);
    bind(AppComponent);
  }
}
Community
  • 1
  • 1
David Parish
  • 111
  • 7
  • You should be able to omit `.call` in `_compiler.call(`. Methods named `call` can be invoked like a function (the same with `viewVactory.call(` (that doesn't change anything with your problem at hand though) – Günter Zöchbauer Oct 12 '14 at 13:03
  • Is injecting the scope as constructor argument still the right way to do it? (I don't use Angular since a while) As far as I remember I read about implementing `ScopeAware` to get the scope. Have you tried to get the scope passid in this way? – Günter Zöchbauer Oct 12 '14 at 13:05
  • Gunter, I was using call just to clarify the code. I'll try ScopeAware. Do I need to call scope.apply()? Is that the missing link here to getting these variables evaluated? – David Parish Oct 13 '14 at 13:11
  • I don't think so. If you read the CHANGLOG at https://pub.dartlang.org/packages/angular you'll see that using a constructor argument to get the scope is invalid in 1.0. I guess you just get an invalid scope (but I'm a bit out of practice in Angular.dart and don't know for sure). – Günter Zöchbauer Oct 13 '14 at 13:14
  • That's odd because NgInclude (which I patterned this off of at first) still takes in Scope as a constructor argument. – David Parish Oct 13 '14 at 13:31
  • Ah I'm on .14. Now I'm not actually writting a component, it's code that "creates" components. I wonder if it will still get a valid scope? – David Parish Oct 13 '14 at 13:36
  • Good question. You'll have to wait for someone who is more involved in Angular.dart than me. – Günter Zöchbauer Oct 13 '14 at 14:47
  • I re-wrote it to implement ScopeAware and that didn't do it. In fact set scope was never called ;( – David Parish Oct 13 '14 at 14:55
  • Did you upgrade to Angular 1.0? – Günter Zöchbauer Oct 13 '14 at 14:56
  • I got this to work. See the code changes in the edits above. It's still not right in my view, but it's much closer. – David Parish Oct 13 '14 at 15:34
  • Your code has still `implements ScopeAware` but also `this._scope` as a constructor argument. I'm pretty sure `ScopeAware` and `set scope(scope);` is the way to go in 1.0. You could implement `AttachAware` and check if `addComponent` is executed before or after `attach` and if it is execute `addComponent` in `attach`. – Günter Zöchbauer Oct 13 '14 at 15:52
  • That was cut and paste error. Removing scope from the constructor but still implementing ScopeAware does not call set on scope. I'll put in the corrected version. – David Parish Oct 13 '14 at 16:26

0 Answers0