I have this to imitate a tree structure:
var MODULESYSTEM =
{
modules:
{
a : function() { return 'modules.a'; }
b : function() { return 'modules.b'; }
c :
{
d : function() { return 'modules.c.d'; }
}
}
}
so MODULESYSTEM.modules.a(); is valid, so MODULESYSTEM.modules.c.d(); too. But what if I want something like MODULESYSTEM.modules.c(); ? It should return 'modules.c'
You won't be able to declare that sort of data structure in one line. You will need to build it up procedurally:
var MODULESYSTEM = {
modules: {
// Other top-level namespace objects
c: function() {
return 'modules.c';
}
}
};
// Later:
MODULESYSTEM.modules.c.d = function() { return 'modules.c.d'; };
There might be a better solution to this problem if you could provide more background about the problem you're looking to solve.
Related
Currently, I have implemented quite standard UNDO and REDO by using listeners to trigger canvas.getObjects() whose JSON output I store in a stack.
// Canvas modified listeners
canvas?.on('object:modified', onCanvasModifiedHandler)
canvas?.on('object:removed', onCanvasModifiedHandler)
canvas?.on('object:changed', onCanvasModifiedHandler)
When the user clicks undo and redo, we fetch JSON representation of the canvas from the stack and loads it using canvas?.loadFromJSON(json, () => { ... })
My problem is that it is quite inefficient to store the entire JSON representation of the canvas when the actual change is quite small. As a result, this approach causes my application to freeze for 500 milliseconds when the user clicks UNDO and REDO.
My proposed solution is to store only the JSON diff by using for example this package, although it is quite an undertaking. https://www.npmjs.com/package/jsondiffpatch
My question is if anyone has had this problem before, and how did you solve it in that case? Or if someone has any other ideas.
Inspired by this thread: https://bountify.co/undo-redo-with-2-canvases-in-fabric-js
I think you need to use the command pattern for this. It will be more efficient than using all JSON data. For that, you need to implement the next approach:
Create a class for storing History. It maybe looks like this
class CommandHistory {
commands = [];
index = 0;
getIndex() {
return this.index;
}
back() {
if (this.index > 0) {
let command = this.commands[--this.index];
command.undo();
}
return this;
}
forward() {
if (this.index < this.commands.length) {
let command = this.commands[this.index++];
command.execute();
}
return this;
}
add(command) {
if (this.commands.length) {
this.commands.splice(this.index, this.commands.length - this.index);
}
this.commands.push(command);
this.index++;
return this;
}
clear() {
this.commands.length = 0;
this.index = 0;
return this;
}
}
// use when you init your Canvas, like this.history = new CommandHistory();
Then you must implement the command classes for your commands.
For adding object
class AddCommand {
constructor(receiver, controller) {
this.receiver = receiver;
this.controller = controller;
}
execute() {
this.controller.addObject(this.receiver);
}
undo() {
this.controller.removeObject(this.receiver);
}
}
// When you will add object on your canvas invoke also this.history.add(new AddCommand(object, controller))
For removing object
class RemoveCommand {
constructor(receiver, controller) {
this.receiver = receiver;
this.controller = controller;
}
execute() {
this.controller.removeObject(this.receiver);
}
undo() {
this.controller.addObject(this.receiver);
}
}
The fabric.js has the saveState method for every object http://fabricjs.com/docs/fabric.Object.html#saveState. And you can use it for implementing the transform command, which will be added to the history object when you will modify your fabric object on the canvas.
class TransformCommand {
constructor(receiver, options = {}) {
this.receiver = receiver;
this._initStateProperties(options);
this.state = {};
this.prevState = {};
this._saveState();
this._savePrevState();
}
execute() {
this._restoreState();
this.receiver.setCoords();
}
undo() {
this._restorePrevState();
this.receiver.setCoords();
}
// private
_initStateProperties(options) {
this.stateProperties = this.receiver.stateProperties;
if (options.stateProperties && options.stateProperties.length) {
this.stateProperties.push(...options.stateProperties);
}
}
_restoreState() {
this._restore(this.state);
}
_restorePrevState() {
this._restore(this.prevState);
}
_restore(state) {
this.stateProperties.forEach((prop) => {
this.receiver.set(prop, state[prop]);
});
}
_saveState() {
this.stateProperties.forEach((prop) => {
this.state[prop] = this.receiver.get(prop);
});
}
_savePrevState() {
if (this.receiver._stateProperties) {
this.stateProperties.forEach((prop) => {
this.prevState[prop] = this.receiver._stateProperties[prop];
});
}
}
}
Now you can add your commands to your history and execute or undo them.
I'm wondering about what's happening when you end up doing something like this in a javascript function:
function() {
var privateMembers : {
'methodA' : function() {
},
'methodB' : function() {
}
};
var publicMembers = {
'methodC' : function() {
},
'methodD' : function() {
}
};
publicMembers.listen = {
"myEvent:Listener" : publicMembers.MethodC;
};
return publicMembers;
}
I understand that returning just publicMembers makes the methods available, however, I'm also wondering would that also make the publicMembers.listen available as well?
yes. these two codes are equivalent
var publicMembers = {
'methodC' : function() {
},
'methodD' : function() {
}
};
publicMembers.listen = {
"myEvent:Listener" : publicMembers.MethodC;
};
and:
var publicMembers = {
'methodC' : function() {
},
'methodD' : function() {
}
'publicMembers' : {
listen : {
"myEvent:Listener" : publicMembers.MethodC;
}
}
};
publicMembers.listen is just a property of publicMembers object, there is no difference between declaring properties using object literals or dot notation. So, when you add listen property to publicMembers object, and you return the whole object, surely you will have access to publicMembers.listen as well. It's pretty easy to test btw.
I'm trying to add a language support in my website and I need to add this code so it will run before marionette render in all the views no matter which type.
onBeforeRender: function(){
var helpers = this.templateHelpers();
this.templateHelpers = function(){
return $.extend( (helpers), {
lang : function () {
return function(val, render) {
return lang(val);
}
}
});
}
}
I don't want to extend all the views and put this code in each of them,
I wonder if there is a way to just put this code in some place and it will run before every render
You should be able to extend the prototype with something like
_.extend(Marionette.View.prototype, {
onBeforeRender: function(){
var helpers = this.templateHelpers();
this.templateHelpers = function(){
return $.extend( (helpers), {
lang : function () {
return function(val, render) {
return lang(val);
}
}
});
}
}
})
Naturally, that means that if one of your marionette views defines its own onBeforeRender, you'll need to call the implementation on the View prototype "by hand".
I think you should create a view mixin with your code and extend every view with this mixin
var LangMixin = {
onBeforeRender: function(){
var helpers = this.templateHelpers();
this.templateHelpers = function(){
return $.extend( (helpers), {
lang : function () {
return function(val, render) {
return lang(val);
}
}
});
}
}
}
var YourView= Backbone.View.extend({
// ...
});
_.extend(YourView.prototype, LangMixin);
I have searched and read for a few hours yet I still cant understand the basic design pattern for creating a new object that has a choice of different methods (of the same name) that is set dependant on one of the arguments. here's some code to explain what I am trying to do.
All advice and alternative approaches welcome. I hope someone can emancipate me form this cloud of ignorance.
Thanks
function BaseConstructor(whichMethods) {
if (whichMethods==='a') {
// do something to incorporate methodSetA
}
else if (whichMethods==='b') {
// do something to incorporate methodSetB
}
this.init();
};
var methodSetA = {
init: function() {
// do initialisation A way
},
speak: function() {
alert('i speak AAA way')
}
};
var methodSetB = {
init: function() {
// do initialisation B way
},
speak: function(){
alert('i got BBB all the way')
}
};
thing = new BaseConstructor('b');
// b is an instance of BaseConstructor and has done the bWay init() function
thing.speak() // return alert 'i got BBB all the way'
You can do it like this using a factory function (a regular function that creates the appropriate object for you):
function BaseConstructor(whichMethods) {
var elem;
if (whichMethods==='a') {
elem = new MethodSetA();
} else if (whichMethods==='b') {
elem = new MethodSetB();
} else {
// figure out what to do here if whichMethods is neither of the previous options
}
elem.init();
return(elem);
};
And invoke it as a regular function call:
var thing = BaseConstructor('b');
thing.speak();
Note: there is no use of new with BaseConstructor() as it's a regular function call.
Well, to do it your way using "method sets," you can iterate and copy into this (here's a demo):
function copy(source, destination) {
for(var x in source) {
if(source.hasOwnProperty(x)) {
destination[x] = source[x];
}
}
}
function BaseConstructor(whichMethods) {
if(whichMethods === 'a') {
copy(methodSetA, this);
} else if(whichMethods === 'b') {
copy(methodSetB, this);
}
this.init();
}
Personally, though, I'd prefer to assign directly to this.
You are looking for factory pattern.
Example:
function objectFactory(whichMethods) {
if (whichMethods==='a') {
return new objectSetA();
}
else if (whichMethods==='b') {
return new objectSetB()
}
};
function objectSetA() {
this.init = function() {
// do initialisation A way
},
this.speak = function() {
alert('i speak AAA way')
}
};
function objectSetB() {
this.init = function() {
// do initialisation B way
},
this.speak = function(){
alert('i got BBB all the way')
}
};
var thing = objectFactory('b');
thing.speak();
In the following code, I want to be able to call bindClickEvents() like so:
App.Utils.Modal.bindClickEvents();
However, I don't understand the syntax necessary to do this.
Current code:
var App = new Object;
App.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Modal.bindClickEvents();
});
You can do it in one go:
var App = {
Modal : {
bindClickEvents : function () {/* ... */}
}
}
or if you want to break that up to separate steps:
var App = {};
App.Modal = {};
Modal.bindClickEvents = function () {/* ... */};
BTW, in reference to your original question title, this is not object chaining. This is object composition. Object chaining is being able to call methods in an object multiple times in a single statement.
Is this what you're trying to do?
var App = {};
App.Utils = {};
App.Utils.Modal = {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
};
$(document).ready(function() {
return App.Utils.Modal.bindClickEvents();
});
Prefer the object literal syntax to the Object constructor; some authors go so far as to call the latter an anti-pattern
Here's the simplest way to set up App.Utils.Modal.bindClickEvents();
var App = {
Utils: {
Modal: {
bindClickEvents: function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
}
}
}
};
Or you can piece it together one step at a time:
var App = {};
App.Utils = {};
App.Utils.Modal = {};
App.Utils.Modal.bindClickEvents = function() {
return $('a.alert-modal').click(function(e) {
return console.log('Alert Callback');
});
};