Compare commits
2 Commits
api-1124c
...
api-toolba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ff2b507e3 | ||
|
|
dde7de01e2 |
@@ -5,6 +5,7 @@ define([
|
||||
'./api/api',
|
||||
'text!./adapter/templates/edit-object-replacement.html',
|
||||
'./ui/Dialog',
|
||||
'./api/events/Events',
|
||||
'./api/objects/bundle'
|
||||
], function (
|
||||
EventEmitter,
|
||||
@@ -12,7 +13,8 @@ define([
|
||||
uuid,
|
||||
api,
|
||||
editObjectTemplate,
|
||||
Dialog
|
||||
Dialog,
|
||||
Events
|
||||
) {
|
||||
function MCT() {
|
||||
EventEmitter.call(this);
|
||||
@@ -100,6 +102,8 @@ define([
|
||||
toolbar: "TOOLBAR"
|
||||
};
|
||||
|
||||
MCT.prototype.events = new Events();
|
||||
|
||||
MCT.prototype.verbs = {
|
||||
mutate: function (domainObject, mutator) {
|
||||
return domainObject.useCapability('mutation', mutator)
|
||||
|
||||
39
src/api/events/EventDecorator.js
Normal file
39
src/api/events/EventDecorator.js
Normal file
@@ -0,0 +1,39 @@
|
||||
define([],
|
||||
function () {
|
||||
function EventDecorator(mct, domainObject) {
|
||||
if (domainObject._proxied){
|
||||
return domainObject;
|
||||
}
|
||||
|
||||
Object.defineProperty(domainObject, "_proxied", {value: true, writable: false, enumerable: false, readable: true});
|
||||
|
||||
var handler = function (path){
|
||||
return {
|
||||
'set': function (target, name, value) {
|
||||
target[name] = value;
|
||||
mct.events.mutation(domainObject).emit([path, name].join("."), value);
|
||||
mct.events.mutation(domainObject).emit("any", value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
function decorateObject(object, property, path) {
|
||||
// Decorate object with a proxy
|
||||
var value = object[property] = new Proxy(object[property], handler(path));
|
||||
|
||||
// Enumerate properties and decorate any sub objects with
|
||||
// proxies
|
||||
Object.keys(object[property]).filter(function (key){
|
||||
return typeof(value[key]) === "object";
|
||||
}).forEach(function (key) {
|
||||
decorateObject(object[property], key, [path, key].join("."));
|
||||
});
|
||||
}
|
||||
|
||||
decorateObject(domainObject, "model", "model");
|
||||
|
||||
return domainObject;
|
||||
}
|
||||
|
||||
return EventDecorator;
|
||||
});
|
||||
31
src/api/events/Events.js
Normal file
31
src/api/events/Events.js
Normal file
@@ -0,0 +1,31 @@
|
||||
define(
|
||||
[
|
||||
"EventEmitter"
|
||||
],
|
||||
function (EventEmitter) {
|
||||
function Events() {
|
||||
this.eventEmitter = new EventEmitter();
|
||||
}
|
||||
|
||||
Events.prototype.mutation = function (domainObject) {
|
||||
var eventEmitter = this.eventEmitter;
|
||||
|
||||
function qualifiedEventName(eventName) {
|
||||
return ['mutation', domainObject.getId(), eventName].join(':');
|
||||
}
|
||||
|
||||
return {
|
||||
on: function(eventName, callback) {
|
||||
return eventEmitter.on(qualifiedEventName(eventName), callback);
|
||||
},
|
||||
emit: function(eventName) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
args.unshift(qualifiedEventName(eventName));
|
||||
|
||||
return eventEmitter.emit.apply(eventEmitter, args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Events;
|
||||
});
|
||||
87
src/api/events/README.md
Normal file
87
src/api/events/README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
* Can't use defineProperty. Will not trigger on properties added to model.
|
||||
Can only use Proxy or setter
|
||||
|
||||
/**
|
||||
* Mutate function on domainObject
|
||||
*/
|
||||
|
||||
domainObject.mutate("property", function (currentVal) {
|
||||
return "12";
|
||||
});
|
||||
|
||||
//No way of mutating multiple properties at once.
|
||||
// Removes need to calculate deltas to trigger
|
||||
/*domainObject.mutate(function (domainObject) {
|
||||
object.foo = "bar";
|
||||
return object;
|
||||
});*/
|
||||
|
||||
//Mutating an array property
|
||||
domainObject.mutate("arrayProperty", function (array) {
|
||||
array.push("foo");
|
||||
array[2] = "bar";
|
||||
//mutate function can calculate delta to decide
|
||||
// whether to trigger update
|
||||
return object;
|
||||
});
|
||||
|
||||
domainObject.mutate("property.nestedProperty", function(nestedValue){
|
||||
return "someNewNestedValue"
|
||||
});
|
||||
|
||||
/*
|
||||
Problem here - no way to prevent arbitrary mutation
|
||||
of the object from within the mutate function.
|
||||
*/
|
||||
domainObject.mutate("property.nestedProperty", function(nestedObject){
|
||||
nestedObject.someprop = "someVal";
|
||||
nestedObject.something = {};
|
||||
nestedObject.something.else = "Hi";
|
||||
return nestedObject;
|
||||
});
|
||||
|
||||
/**
|
||||
* Hybrid setter / mutator approach
|
||||
*/
|
||||
domainObject.mutate("property", "value");
|
||||
domainObject.mutate("property.nestedProperty", "value");
|
||||
domainObject.mutate("arrayProperty", function(array){
|
||||
array.push("foo");
|
||||
array[1] = "bar";
|
||||
});
|
||||
// OR
|
||||
// Mutate the array outside and use the same approach
|
||||
domainObject.mutate("arrayProperty", arrayVal);
|
||||
/**
|
||||
* Setter function on domainObject. I think this is
|
||||
* out because of the difficulty in support Arrays
|
||||
*/
|
||||
//Setters are out because of arrays I think
|
||||
domainObject.set("property", value);
|
||||
domainObject.set("property.nestedProperty", value);
|
||||
|
||||
domainObject.on("configuration.layout");
|
||||
domainObject.on("configuration.layout.positions");
|
||||
|
||||
// The following would trigger configuration.views
|
||||
// watcher, but not configuration.views.view1
|
||||
domainObject.set("configuration.layout", {"positions": [{"123-1234-1232-123": [100, 200]}]});
|
||||
|
||||
// Don't think this will work, it's an unnatural way
|
||||
// of using js.
|
||||
|
||||
//Using a setter for everything requires unnatural
|
||||
// use of javascript
|
||||
domainObject.set("configuration", {})
|
||||
.set("layout", {})
|
||||
.set("positions", [{"123-1234-1232-123": [100, 200]}]);
|
||||
|
||||
// VanillaJS
|
||||
domainObject.model.configuration = {};
|
||||
domainObject.model.configuration.layout = {};
|
||||
domainObject.model.configuration.positions = [{"123-1234-1232-123": [100, 200]}];
|
||||
|
||||
// Layout does exist
|
||||
domainObject.set("configuration")
|
||||
.set("layout")
|
||||
.set("positions", [{"123-1234-1232-123": [100, 200]}]);
|
||||
@@ -3,8 +3,9 @@ define([
|
||||
"text!./todo-task.html",
|
||||
"text!./todo-toolbar.html",
|
||||
"text!./todo-dialog.html",
|
||||
"../../src/api/events/EventDecorator",
|
||||
"zepto"
|
||||
], function (todoTemplate, taskTemplate, toolbarTemplate, dialogTemplate, $) {
|
||||
], function (todoTemplate, taskTemplate, toolbarTemplate, dialogTemplate, eventDecorator, $) {
|
||||
/**
|
||||
* @param {mct.MCT} mct
|
||||
*/
|
||||
@@ -44,6 +45,10 @@ define([
|
||||
this.render();
|
||||
|
||||
mct.verbs.observe(this.domainObject, this.render.bind(this));
|
||||
|
||||
mct.events.mutation(this.domainObject).on("*", function (value) {
|
||||
console.log("model changed");
|
||||
});
|
||||
};
|
||||
|
||||
TodoView.prototype.destroy = function () {
|
||||
@@ -83,35 +88,37 @@ define([
|
||||
}
|
||||
};
|
||||
var filterValue = this.filterValue;
|
||||
var model = domainObject.model;
|
||||
|
||||
Object.keys($buttons).forEach(function (k) {
|
||||
$buttons[k].toggleClass('selected', filterValue === k);
|
||||
});
|
||||
tasks = tasks.filter(filters[filterValue]);
|
||||
|
||||
$list.empty();
|
||||
tasks.forEach(function (task, index) {
|
||||
var $taskEls = $(taskTemplate);
|
||||
var $checkbox = $taskEls.find('.example-task-checked');
|
||||
$checkbox.prop('checked', task.completed);
|
||||
$taskEls.find('.example-task-description')
|
||||
.text(task.description);
|
||||
|
||||
$checkbox.on('change', function () {
|
||||
var checked = !!$checkbox.prop('checked');
|
||||
mct.verbs.mutate(domainObject, function (model) {
|
||||
model.tasks[index].completed = checked;
|
||||
function renderTasks() {
|
||||
$list.empty();
|
||||
domainObject.getModel().tasks.forEach(function (task, index) {
|
||||
var $taskEls = $(taskTemplate);
|
||||
var $checkbox = $taskEls.find('.example-task-checked');
|
||||
$checkbox.prop('checked', task.completed);
|
||||
$taskEls.find('.example-task-description')
|
||||
.text(task.description);
|
||||
$checkbox.on('change', function () {
|
||||
var checked = !!$checkbox.prop('checked');
|
||||
domainObject.getModel().tasks[index].completed = checked;
|
||||
});
|
||||
});
|
||||
|
||||
$list.append($taskEls);
|
||||
});
|
||||
$list.append($taskEls);
|
||||
});
|
||||
}
|
||||
|
||||
renderTasks();
|
||||
|
||||
$message.toggle(tasks.length < 1);
|
||||
|
||||
mct.events.mutation(domainObject).on("model.tasks.length", renderTasks);
|
||||
};
|
||||
|
||||
|
||||
|
||||
function TodoToolbarView(domainObject) {
|
||||
this.domainObject = domainObject;
|
||||
}
|
||||
@@ -135,10 +142,11 @@ define([
|
||||
|
||||
mct.dialog(view, "Add a Task").then(function () {
|
||||
var description = $dialog.find('input').val();
|
||||
mct.verbs.mutate(domainObject, function (model) {
|
||||
domainObject.getModel().tasks.push({ description: description });
|
||||
/*mct.verbs.mutate(domainObject, function (model) {
|
||||
model.tasks.push({ description: description });
|
||||
console.log(model);
|
||||
});
|
||||
});*/
|
||||
});
|
||||
});
|
||||
$remove.on('click', window.alert.bind(window, "Remove!"));
|
||||
@@ -150,9 +158,10 @@ define([
|
||||
|
||||
mct.type('example.todo', todoType);
|
||||
mct.view(mct.regions.main, function (domainObject) {
|
||||
return todoType.check(domainObject) && new TodoView(domainObject);
|
||||
return todoType.check(domainObject) && new TodoView(eventDecorator(mct, domainObject));
|
||||
});
|
||||
mct.view(mct.regions.toolbar, function (domainObject) {
|
||||
domainObject = eventDecorator(mct, domainObject);
|
||||
return todoType.check(domainObject) && new TodoToolbarView(domainObject);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user