Compare commits

...

2 Commits

Author SHA1 Message Date
Henry
7ff2b507e3 Added examples 2016-07-01 11:03:53 -07:00
Henry
dde7de01e2 Object events prototype 2016-07-01 11:03:52 -07:00
5 changed files with 192 additions and 22 deletions

View File

@@ -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)

View 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
View 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
View 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]}]);

View File

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