0

我已经为 Dart 移植了一个方便的 JS 库:dartscale。它的功能的关键部分可以分解为:

final Map<Symbol, ClassMirror> _registeredModules = new Map<Symbol, ClassMirror>();

register(module, [String moduleName]) {
   final uniqueModuleName = moduleName != null ? moduleName : module.runtimeType.toString();
   final Symbol uniqueModuleIdentifier = new Symbol(uniqueModuleName);

   if (_registeredModules.containsKey(uniqueModuleName)) {
     throw new StateError("Module ${moduleName} already registered!");
   }

   final ClassMirror mirror = reflect(module).type;
  _registeredModules[uniqueModuleIdentifier] = mirror;
}

start(Symbol moduleName, String id, options) {
   if (!_registeredModules.containsKey(moduleName)) {
      throw new StateError("Module ${moduleName} not registered!");
   }

   final ClassMirror mirror = _registeredModules[moduleName];
   final Symbol moduleId = id != null ? new Symbol(id) : moduleName;
   final Sandbox sandbox = new Sandbox(this.mediator);

   if (_runningModules.containsKey(moduleId)) {
      throw new StateError("Module with id #${moduleId} already running!");
   }

   final InstanceMirror moduleInstance = mirror.newInstance(new Symbol(''), [sandbox], null);
   moduleInstance.invoke(new Symbol("start"), [options]);

   _runningModules[moduleId] = moduleInstance;
}

我也举个例子

part of example;

class ToDos {

    Sandbox _sandbox;

    DivElement _contentEl;
    int _nextToDoId = 0;

    UListElement _todoList;

     ToDos ([Sandbox this._sandbox]);

     start([Map options]) {
         this._initLocalStorage();

         var html = ['<div id="module-todos">',
                       '<form>',
                          '<input type="text" class="input-medium">',
                          '<button type="submit" class="btn">Add</button>',
                       '</form>',
                       '<ul>',
                       '</ul>',
                     '</div>'].join('');

         this._contentEl = new Element.html(html);
         this._todoList = this._contentEl.query('ul');

         options['containerEl'].append(this._contentEl);

         window.localStorage.keys.forEach((key) => this._renderToDo(key));

         this._setupEvents();

         this._sandbox.channel('navigationbar').topic('filter').listen((filterContent) {
             this._filter(filterContent);
         });

        this._sandbox.channel('navigationbar').topic('clearfilter').listen((filterContent) {
           this._todoList.queryAll('li span').forEach((element) => element.parent.classes.remove('hide'));
        });
    }

    stop() {
        this._contentEl.remove();
    }

    _initLocalStorage() {
        if (window.localStorage.keys.length == 0) {
            var map = {
                "1": {
                    "subject": "Groceries: Banas and Apples",
                    "isDone": false
                },
                "2": {
                    "subject": "Taxes: take care of them",
                    "isDone": false
                },
                "3": {
                    "subject": "Bring out trash",
                    "isDone": false
                }  
            };

            for (var key in map.keys) {
                window.localStorage[key] = stringify(map[key]);
                this._nextToDoId++;
            }
        }
        else {
           for (var key in window.localStorage.keys) {
              var intKey = int.parse(key);

              if (intKey > this._nextToDoId) {
                  this._nextToDoId = intKey;
              }

              this._nextToDoId++;
           }
       }
   }

   _setupEvents() {
       var input = this._contentEl.query('input');

       input.onKeyDown.listen((event) {
          if (event.keyCode == KeyCode.ENTER) {
              event.preventDefault();

              this._addToDo(input.value);
              input.value = '';
          }
       });

       this._contentEl.query('button[type="Submit"]').onClick.listen((event) {
           event.preventDefault();

           if (input.value.length > 0) {
               this._addToDo(input.value);
               input.value = '';
           }
       });

       this._todoList.onClick.listen((MouseEvent event) {
           var el = event.target;

           if (el.classes.contains('icon-remove')) {
               this._deleteToDo(el.parent);
           }
           else if (el.classes.contains('icon-ok')) {
               this._toggleToDoDone(el.parent);
           }
       });
   }

   _renderToDo(id) {
       var todoObject = parse(window.localStorage[id.toString()]);

       var html = ['<li class="record-todo ', todoObject["isDone"]?"done":"",'" data-id="', id,'">',
                      '<span>', todoObject["subject"], '</span>',
                      '<i class="icon icon-ok"></i>',
                      '<i class="icon icon-remove"></i>',
                  '</li>'].join('');

       this._todoList.append(new Element.html(html));
   }

   _addToDo(text) {
       var todoJson = stringify({
           "subject": text,
           "isDone": false
       });

       window.localStorage[this._nextToDoId.toString()] = todoJson;
       this._renderToDo(this._nextToDoId);

       this._nextToDoId++;
   }

   _deleteToDo(todoLIElement) {
       window.localStorage.remove(todoLIElement.dataset["id"]);

       todoLIElement.remove();
   }

   _toggleToDoDone(todoLIElement) {
       var done = !todoLIElement.classes.contains('done'); 
       var id = todoLIElement.dataset["id"];
       var todoObject = parse(window.localStorage[id]);
       todoObject["isDone"] = done;
       window.localStorage[id] = stringify(todoObject);

       if (done) {
          todoLIElement.classes.add('done');
       }
       else {
          todoLIElement.classes.remove('done');
       }
   }

   _filter(content) {
       this._todoList.queryAll('li span').forEach((element) {
            if (element.innerHtml.contains(content)) {
                element.parent.classes.remove('hide');
            }
            else {
               element.parent.classes.add('hide');
            }
       });
   }
}

在我的 App.dart

library example;

import 'dart:html';
import 'dart:json';
import '../lib/dartscale.dart';

part 'dart/ToDos.dart';

main () {
    var core = new Core();
    core.register(new ToDos());

    core.start("ToDos", "ToDos", {
        "containerEl": query('body > .container')
    });
}

dart2js 中的错误?

4

2 回答 2

0

Solution

Turns out that dart2js has problems with mirrored calls on Methods/Constructors which have optional positional Arguments. So changing

class ToDos {
   Sandbox _sandbox;
   ToDos([Sandbox this._sandbox]);
}

to

class ToDos {
   Sandbox _sandbox;
   ToDos(Sandbox this._sandbox); //make the argument non-optional
}

solved my Problem

于 2013-07-31T17:37:11.343 回答
0

这并不是真正的答案,因为您没有说明出了什么问题,而是一些一般性建议。大多数情况下,如果您可以轻松避免它们,我会避免new Symbol()和镜像,在这种情况下您可以。

首先,您应该弄清楚是否要注册模块实例或按需生成实例,您可能不希望两者都像您在这里一样。如果您注册了一个实例,那么您不能重复使用该实例吗?是否start()需要生成新实例作为其规范的一部分?您转身尝试确保实例尚未运行。

如果你真的需要生产实例,一个简单的工厂函数将消除对镜像的需要。所以而不是:

core.register(new ToDos());

你写:

core.register('ToDos', () => new ToDos());

如果还想使用镜像,可以清理使用new Symbol(). 这里有一些建议:

  • 不要使用 Symbols 作为键,除非你真的是从反射 API 中获取它们,比如 mirrors 和 noSuchMethod。只需使用字符串名称或 runtimeType。在您的情况下,您将符号和字符串混合为_registeredModules地图中的键,这可能会导致一些错误,例如模块似乎永远不会被注册。(您是否在检查模式下进行测试?)
  • 不使用new Symbol('name'),使用const Symbol('name')
  • 当您可以直接调用方法时,请勿使用 InstanceMirror.invoke、getField 或 setField。在您的代码中,您可以替换

    moduleInstance.invoke(new Symbol("start"), [options]);
    

    moduleInstance.reflectee.start(options);
    
  • 工厂并不邪恶。从类型实例调用构造函数会很好,但在此之前,在 Dart 中注册工厂是相当轻量级的。

这是包含这些建议的代码:

typedef Object Factory(Sandbox sandbox);

final Map<Symbol, Factory> _registeredModules = new Map<Type, Factory>();

register(Type type, Factory factory) {
   if (_registeredModules.containsKey(type)) {
     throw new StateError("Module $type already registered!");
   }
   _registeredModules[type] = factory;
}

start(Type type, options) {
   if (!_registeredModules.containsKey(type)) {
      throw new StateError("Module $type not registered!");
   }
   if (_runningModules.containsKey(type)) {
      throw new StateError("Module $type already running!");
   }
   Sandbox sandbox = new Sandbox(this.mediator);    
   var module = _runningModules[type](sandbox)..start(options);
   _runningModules[type] = module;
}
于 2013-07-29T02:59:30.263 回答