Преглед изворни кода

add Seed.broadcast() and scope.$on()

Evan You пре 13 година
родитељ
комит
2658486bcc
5 измењених фајлова са 81 додато и 31 уклоњено
  1. 20 22
      examples/todomvc/js/app.js
  2. 10 2
      src/main.js
  3. 36 4
      src/scope.js
  4. 13 3
      src/seed.js
  5. 2 0
      src/utils.js

+ 20 - 22
examples/todomvc/js/app.js

@@ -1,10 +1,25 @@
+window.addEventListener('hashchange', function () {
+    Seed.broadcast('filterchange')
+})
+
 Seed.controller('todos', function (scope) {
 
+    // filters ----------------------------------------------------------------
+    var filters = {
+        all: function () { return true },
+        active: function (todo) { return !todo.completed },
+        completed: function (todo) { return todo.completed }
+    }
+    var updateFilter = function () {
+        var filter = location.hash.slice(2)
+        scope.filter = (filter in filters) ? filter : 'all'
+    }
+    updateFilter()
+    scope.$on('filterchange', updateFilter)
+
     // regular properties -----------------------------------------------------
     scope.todos = todoStorage.fetch()
-    scope.remaining = scope.todos.reduce(function (n, todo) {
-        return n + (todo.completed ? 0 : 1)
-    }, 0)
+    scope.remaining = scope.todos.filter(filters.active).length
 
     // computed properties ----------------------------------------------------
     scope.total = {get: function () {
@@ -17,7 +32,7 @@ Seed.controller('todos', function (scope) {
 
     // dynamic context computed property using info from target scope
     scope.todoFiltered = {get: function (ctx) {
-        return filters[scope.filter](ctx.scope.completed)
+        return filters[scope.filter](ctx.scope)
     }}
 
     // dynamic context computed property using info from target element
@@ -80,26 +95,9 @@ Seed.controller('todos', function (scope) {
     }
 
     scope.removeCompleted = function () {
-        scope.todos = scope.todos.filter(function (todo) {
-            return !todo.completed
-        })
+        scope.todos = scope.todos.filter(filters.active)
         todoStorage.save(scope.todos)
     }
-
-    // filters ----------------------------------------------------------------
-    var filters = {
-        all: function () { return true },
-        active: function (v) { return !v },
-        completed: function (v) { return v }
-    }
-
-    function updateFilter () {
-        scope.filter = location.hash ? location.hash.slice(2) : 'all'
-    }
-
-    updateFilter()
-    window.addEventListener('hashchange', updateFilter)
-
 })
 
 Seed.bootstrap()

+ 10 - 2
src/main.js

@@ -5,7 +5,8 @@ var config      = require('./config'),
     textParser  = require('./text-parser'),
     utils       = require('./utils')
 
-var controllers = config.controllers,
+var eventbus    = utils.eventbus,
+    controllers = config.controllers,
     datum       = config.datum,
     api         = {},
     reserved    = ['datum', 'controllers'],
@@ -16,6 +17,13 @@ var controllers = config.controllers,
  */
 api.utils = utils
 
+/*
+ *  broadcast event
+ */
+api.broadcast = function () {
+    eventbus.emit.apply(eventbus, arguments)
+}
+
 /*
  *  Store a piece of plain data in config.datum
  *  so it can be consumed by sd-data
@@ -68,7 +76,7 @@ api.config = function (opts) {
  *  Compile a single element
  */
 api.compile = function (el) {
-    new Seed(el)
+    return new Seed(el).scope
 }
 
 /*

+ 36 - 4
src/scope.js

@@ -1,15 +1,47 @@
 var utils   = require('./utils')
 
+/*
+ *  Scope is the ViewModel/whatever exposed to the user
+ *  that holds data, computed properties, event handlers
+ *  and a few reserved methods
+ */
 function Scope (seed, options) {
     this.$seed     = seed
     this.$el       = seed.el
     this.$index    = options.index
     this.$parent   = options.parentSeed && options.parentSeed.scope
-    this.$watchers = {}
+    this.$seed._watchers = {}
 }
 
 var ScopeProto = Scope.prototype
 
+/*
+ *  register a listener that will be broadcasted from the global event bus
+ */
+ScopeProto.$on = function (event, handler) {
+    utils.eventbus.on(event, handler)
+    this.$seed._listeners.push({
+        event: event,
+        handler: handler
+    })
+}
+
+/*
+ *  remove the registered listener
+ */
+ScopeProto.$off = function (event, handler) {
+    utils.eventbus.off(event, handler)
+    var listeners = this.$seed._listeners,
+        i = listeners.length, listener
+    while (i--) {
+        listener = listeners[i]
+        if (listener.event === event && listener.handler === handler) {
+            listeners.splice(i, 1)
+            break
+        }
+    }
+}
+
 /*
  *  watch a key on the scope for changes
  *  fire callback with new value
@@ -21,7 +53,7 @@ ScopeProto.$watch = function (key, callback) {
         var scope   = self.$seed.scope,
             binding = self.$seed._bindings[key],
             i       = binding.deps.length,
-            watcher = self.$watchers[key] = {
+            watcher = self.$seed._watchers[key] = {
                 refresh: function () {
                     callback(scope[key])
                 },
@@ -39,14 +71,14 @@ ScopeProto.$watch = function (key, callback) {
 ScopeProto.$unwatch = function (key) {
     var self = this
     setTimeout(function () {
-        var watcher = self.$watchers[key]
+        var watcher = self.$seed._watchers[key]
         if (!watcher) return
         var i = watcher.deps.length, subs
         while (i--) {
             subs = watcher.deps[i].subs
             subs.splice(subs.indexOf(watcher))
         }
-        delete self.$watchers[key]
+        delete self.$seed._watchers[key]
     }, 0)
 }
 

+ 13 - 3
src/seed.js

@@ -3,7 +3,8 @@ var config          = require('./config'),
     Binding         = require('./binding'),
     DirectiveParser = require('./directive-parser'),
     TextParser      = require('./text-parser'),
-    depsParser      = require('./deps-parser')
+    depsParser      = require('./deps-parser'),
+    eventbus        = require('./utils').eventbus
 
 var slice           = Array.prototype.slice,
     ctrlAttr        = config.prefix + '-controller',
@@ -24,6 +25,8 @@ function Seed (el, options) {
     this.el               = el
     el.seed               = this
     this._bindings        = {}
+    this._watchers        = {}
+    this._listeners       = []
     // list of computed properties that need to parse dependencies for
     this._computed        = []
     // list of bindings that has dynamic context dependencies
@@ -254,14 +257,21 @@ SeedProto._bindContexts = function (bindings) {
  *  to remove event listeners, destroy child seeds, etc.
  */
 SeedProto._unbind = function () {
-    var i, ins
-    for (var key in this._bindings) {
+    var i, ins, key, listener
+    // unbind all bindings
+    for (key in this._bindings) {
         ins = this._bindings[key].instances
         i = ins.length
         while (i--) {
             if (ins[i].unbind) ins[i].unbind()
         }
     }
+    // remove all listeners on eventbus
+    i = this._listeners.length
+    while (i--) {
+        listener = this._listeners[i]
+        eventbus.off(listener.event, listener.handler)
+    }
 }
 
 /*

+ 2 - 0
src/utils.js

@@ -51,6 +51,8 @@ function dump (val) {
 
 module.exports = {
 
+    // the global event bus
+    eventbus: new Emitter(),
     typeOf: typeOf,
     dump: dump,