Is there a way to have a static variable in a dojo (1.8) module so that I can retain state?
Example, say I set some value in some/module
require([
'some/module'
], function (module) {
module.setSomeValue(3);
});
.. and then want to retrieve it later
define([
'some/module'
], function(module) {
return {
start: function() {
var x = module.getSomeValue();
}
};
});
A solution that works but seems like a hack,
acompany = window.acompany || {};
acompany.project = acompany.project || {
};
require([
], function() {
var debug = false;
acompany.project.module = {
/* static variables and functions here */
};
});
define([
], function () {
return acompany.project.module;
});
Actually there is always only single instance of AMD module, because (source):
define has two additional important characteristics that may not be immediately obvious:
Module creation is lazy and asynchronous, and does not occur immediately when define is called. This means that factory is not executed, and any dependencies of the module will not be resolved, until some running code actually requires the module.
Once a module value has been entered into the module namespace, it is not recomputed each time it is demanded. On a practical level, this means that factory is only invoked once, and the returned value is cached and shared among all code that uses a given module. (Note: the dojo loader includes the nonstandard function require.undef, which undefines a module value.)
In addition you do not have to provide just factory function, you can provide an object literal as well:
define("some/module", {
someValue: "some",
otherValue: "other"
});
Then somewhere else in your code:
require(["some/module"], function(module) {
console.log("module.someValue", module.someValue); // some
module.someValue = "some changed";
});
require(["some/module"], function(module) {
console.log("module.someValue", module.someValue); // some changed
});
More robust solution includes an instance of dojo/Stateful, so you can watch for changes and define custom setters and getters:
define("some/stateful-module", ["dojo/Stateful"], function(Stateful){
var stateful = new Stateful({
someValue: "some",
otherValue: "other"
});
return stateful;
});
Then somewhere else in your code:
require(["some/stateful-module"], function(module) {
console.log("stateful-module.someValue:", module.get("someValue"));
module.watch(function(name, oldValue, newValue) {
console.log("stateful-module: property"
, name
, "changed from"
, "'" + oldValue + "'"
, "to"
, "'" + newValue + "'"
);
});
});
require(["some/stateful-module"], function(module) {
module.set("someValue", "some changed");
});
See how it works at jsFiddle: http://jsfiddle.net/phusick/fHvZf/. It's in a single file there, but it will work the same way across the whole application unless you require.undef(mid) the module.
There are multiple files in Dojo like dojo/date/locale that define static variables and functions and not widgets/classes using dojo.declare.
Define the module
define([
'dojo/_base/lang/',
'some/module'
], function(lang, module) {
var m = lang.getObject('some.module', true);
m.x = 0;
m.doSomething = function(){
// doSomething
};
return m;
});
Use the module
require([
'some/module'
], function(someModule) {
var debug = false;
/* someModule - static variables and functions here */
if(someModule.x == 0){
}
});
Related
I have this vue component:
export default {
data: function() {
return {
editor: new Editor({
//some options
}),
}
},
computed: {
doc(){ // <--------------------- take attention on this computed property
return this.editor ? this.editor.view.state.doc : null;
},
},
watch: {
doc: {
handler: function(val, OldVal){
// call some this.editor methods
},
deep: true,
immediate: true,
},
},
}
Will computed property doc be dependent on a data property editor if I use this.editor only for checking if it is defined and not use it for assigning it to the doc? I mean, If I will change this.editor will doc be changed? Also, I have watcher on doc so I need to know if I will cause an infinite loop.
In the doc property computation, you use:
the editor property (at the beginning of your ternary, this.editor ? ...)
if editor exists, the editor.view.state.doc property
So the computation of doc will be registered by Vue reactivity system as an effect related to the properties editor and (provided that editor exists) to editor.view.state.doc. In other words, the doc property will be reevaluated each time one of these two properties changes.
=> to reply to the initial question, doc will indeed depend on editor.
This can be toned though, because by 'property change', we mean:
for properties of primitive types, being reassigned with a different value
for objects, having a new reference
So, in our case, if editor, which is an object, is just mutated, and that this mutation does not concern it's property editor.view.state.doc, then doc will not be reevaluated. Here are few examples:
this.editor = { ... } // doc will be reevaluated
this.editor.name = ' ... ' // doc will NOT be reevaluated
this.editor.view.state.doc = { ... } // doc will be reevaluated
If you want to understand this under the hood, I would recommand these resources (for Vue 3):
the reactivity course on Vue Mastery (free)
this great talk and demo (building a simple Vue-like reactivity system)
About the inifinite loop, the doc watcher handler will be executed only:
if doc is reassigned with a different value
in the case where docis an object, if doc is mutated (since you applied the deep option to the doc watcher)
The only possibility to trigger an infinite loop would be to, in the doc watcher handler, mutate or give a new value to doc (or editor.view.state.doc). For example (cf #Darius answer):
watch: {
doc: {
handler: function(val, OldVal){
// we give a new ref each time this handler is executed
// so this will trigger an infinite loop
this.editor.view.state.doc = {}
},
// ...
},
}
=> to reply to the second question, apart from these edge cases, your code won't trigger a loop. For example:
watch: {
doc: {
handler: function(val, OldVal){
// even if we mutate the editor object, this will NOT trigger a loop
this.editor.docsList = []
},
// ...
},
}
Changing editor variable should work, but changing Editor content may not, as it depends on Editor class and how it respects reactivity.
For example:
export default {
data: function() {
return {
editor: {text: '' }
}
}
}
...
this.editor.text = 'Text' // works
this.editor.text = {param: ''} // works
this.editor.text.param = 'value' // works
this.editor.param = {} // does't work, as creation of new property is not observable
If editor observer works and you are changing editor property in observer, which 'reinitializes' internal structures, it may lead to infinite loop:
var Editor = function() {
this.document = {}
this.change = () => { this.document = {} }
}
var data = new Vue({
data: () => ({
editor: new Editor(),
check: 0
}),
watch: {
editor: {
handler() {
this.check++
console.log('Changed')
if (this.check < 5)
this.editor.change()
else
console.log('Infinite loop!')
},
deep: true,
immediate: true
}
}
})
data.editor.change()
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
In such case, extra checking is necessary before making the change.
How do I change the template of a widget, using mixins dijit/_TemplatedMixin and dijit/_WidgetsInTemplateMixin, at a later time (not in the constructor)?
My scenario is that the widget must make a call to the server to get data, and the callback function will then merge the data with a template file and then the resulting template should be used for the templateString. The widget should update its contents at this point.
Setting the templateString and calling buildRendering() has no effect.
Here is a simplified version of my code:
define([
"dojo/_base/declare",
"dojo/_base/lang",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
],
function(declare, lang, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin) {
return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
constructor: function(id) {
this.id = id;
this.templateString = "<div>Loading...</div>";
//use xhr to call REST service to get data.
//dataLoadedCallback is executed with response data
...
},
dataLoadedCallback : function(data) {
this.destroyRendering();
//render a templateString using the data response from the rest call
this.templateString = "<div>Data is loaded. Name:" + data.name + "</div>"
this.buildRendering();
},
});
});
You cannot do such thing. The template is parsed only once before postCreate method.
However there is few things you can do:
Create a non-ui widget which will do the XHR call. When this non-ui widget get the XHR response it creates the UI widget with the correct templateString
Or use dojo/dom-construct. It contains a toDom method which you can use for converting your string into nodes. Then you can append that to the widget.
Note: this will not parse any data-dojo attributes
You could also directly inject the received templateString into the widget domNode:
dataLoadedCallback : function(data) {
this.domNode.innerHTML = "<div>Data is loaded. Name:" + data.name + "</div>";
//you might be able to parse the content (if you have subwidgets) using dojo/parse
},
Last but not least, here is a util I wrote for my self. It allow to parse any templateString at any time (like dojo does on widget creation)
define([
'dojo/dom-construct',
'dojo/string',
'dijit/_AttachMixin',
'dijit/_TemplatedMixin'
], function(domConstruct, string,
_AttachMixin, _TemplatedMixin) {
// summary:
// provide an utility to parse a template a runtime (and create attach point, atach events, etc...)
// Copyright: Benjamin Santalucia
var GET_ATTRIBUTE_FUNCTION = function(n, p) { return n.getAttribute(p); },
_TemplateParserMixin = function() {};
_TemplateParserMixin.prototype = {
parseTemplate: function(template, data, container, position, transformer) {
// summary:
// parse the template exactly as dojo will nativly do with a templateString
if(this._attachPoints === undefined) {
this._attachPoints = [];
}
if(this._attachEvents === undefined) {
this._attachEvents = [];
}
var nodes,
x,
len,
newTemplate = string.substitute(template, data, transformer),
node = domConstruct.toDom(_TemplatedMixin.prototype._stringRepl.call(this, newTemplate));
if(node.nodeName === '#document-fragment') {
node = node.firstChild;
}
//parse all nodes and create attach points and attach events
nodes = node.getElementsByTagName('*');
len = nodes.length;
for(x = -1; x < len; x++) {
_AttachMixin.prototype._processTemplateNode.call(this, x < 0 ? node : nodes[x], GET_ATTRIBUTE_FUNCTION, _AttachMixin.prototype._attach);
}
if(container) {
domConstruct.place(node, container, position);
}
return node;
}
};
return _TemplateParserMixin;
});
Usage is:
returnedNode = w.parseTemplate(newTemplateString, {
templatePlaceHolderName: 'foo' //for teplate with placeholders like ${templatePlaceHolderName}
}, domNodeToInsertIn, 'only'); //last parameter is same as dojo/dom-construct::place() >> last, first, before, after, only
Today, I have an config for the translateProvider looking like this:
App.config(['$translateProvider', function ($translateProvider) {
$translateProvider.preferredLanguage('en-US');
$translateProvider.useLoader('TranslationLoader', { versionIdentifier : 127} );
$translateProvider.useMissingTranslationHandler('MissingTranslationHandler');
}]);
The problem is that I don't know the value of the formIdentifier-option at configuration time. I get this value after resolving the first state in ui-route. I've tried to set the translationProvides loader in the state's controller, but realized that that's not possible :)
Any ideas?
angular-translate allows you to use any service as a loader as long as it meets a desired interface. But it doesn't restrict you in ways of how you pass additional parameters to the loader. So, you may pass them just like you want.
For example, you can set additional parameters directly to the loader. Just implement setters for them on top of your loader:
module.factory('Loader', [
'$q',
function($q) {
var myParam;
var loader = function(options) {
var allParams = angular.extend({}, { myParam: myParam }, options);
var deferred = $q.defer();
// load stuff
return deferred.promise;
};
loader.setMyParam = function(param) {
myParam = param;
};
return loader;
}])
Also, you may try to set these parameters with some helper service (either sync or async:
module.factory('SyncLoader', [
'$q', '$injector',
function($q, $injector) {
var loader = function(options) {
var helper = $injector.get(options.helper);
var myParam = helper.getMyParam();
var deferred = $q.defer();
// load stuff
return deferred.promise;
};
return loader;
}]);
or
module.factory('AsyncLoader', [
'$q', '$injector',
function($q, $injector) {
var loader = function(options) {
var helper = $injector.get(options.helper);
var deferred = $q.defer();
helper.getMyParam()
.then(function success(myParam) {
// load stuff
}, function error() {
// fail, probably
});
return deferred.promise;
};
return loader;
}]);
Also, it might be possible to use events somehow. Or, maybe, there are some other ways possible. It depends on a specific architecture.
In my ExtJS 4.0.7 app I have some 3rd party javascripts that I need to dynamically load to render certain panel contents (some fancy charting/visualization widgets).
I run in to the age-old problem that the script doesn't finish loading before I try to use it. I thought ExtJS might have an elegant solution for this (much like the class loader: Ext.Loader).
I've looked at both Ext.Loader and Ext.ComponentLoader, but neither seem to provide what I'm looking for. Do I have to just "roll my own" and setup a timer to wait for a marker variable to exist?
Here's an example of how it's done in ExtJS 4.1.x:
Ext.Loader.loadScript({
url: '...', // URL of script
scope: this, // scope of callbacks
onLoad: function() { // callback fn when script is loaded
// ...
},
onError: function() { // callback fn if load fails
// ...
}
});
I've looked at both Ext.Loader and Ext.ComponentLoader, but neither
seem to provide what I'm looking for
Really looks like it's true. The only thing that can help you here, I think, is Loader's injectScriptElement method (which, however, is private):
var onError = function() {
// run this code on error
};
var onLoad = function() {
// run this code when script is loaded
};
Ext.Loader.injectScriptElement('/path/to/file.js', onLoad, onError);
Seems like this method would do what you want (here is example). But the only problem is that , ... you know, the method is marked as private.
This is exactly what newest Ext.Loader.loadScript from Ext.4-1 can be used for.
See http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Loader-method-loadScript
For all you googlers out there, I ended up rolling my own by borrowing some Ext code:
var injectScriptElement = function(id, url, onLoad, onError, scope) {
var script = document.createElement('script'),
documentHead = typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),
cleanupScriptElement = function(script) {
script.id = id;
script.onload = null;
script.onreadystatechange = null;
script.onerror = null;
return this;
},
onLoadFn = function() {
cleanupScriptElement(script);
onLoad.call(scope);
},
onErrorFn = function() {
cleanupScriptElement(script);
onError.call(scope);
};
// if the script is already loaded, don't load it again
if (document.getElementById(id) !== null) {
onLoadFn();
return;
}
script.type = 'text/javascript';
script.src = url;
script.onload = onLoadFn;
script.onerror = onErrorFn;
script.onreadystatechange = function() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
onLoadFn();
}
};
documentHead.appendChild(script);
return script;
}
var error = function() {
console.log('error occurred');
}
var init = function() {
console.log('should not get run till the script is fully loaded');
}
injectScriptElement('myScriptElem', 'http://www.example.com/script.js', init, error, this);
From looking at the source it seems to me that you could do it in a bit of a hackish way. Try using Ext.Loader.setPath() to map a bogus namespace to your third party javascript files, and then use Ext.Loader.require() to try to load them. It doesn't look like ExtJS actually checks if required class is defined in the file included.
I am actually playing with Javascript doing a small game and I would like to implement what I've found on http://www.crockford.com/javascript/inheritance.html which is something similar to:
ZParenizor.method('toString', function () {
if (this.getValue()) {
return this.uber('toString');
}
return "-0-";
});
I can't find any reference the the library used to make such development possible. Any ideas? Otherwise, I'm looking for a good library that will aid my OOP developments.
Thank you
Edit:
I am looking for a OOP solution / library for Node.js. Please note that I'm new to Node.js
2 months later
Maybe you do need a library, ES5 is verbose as hell so I've created pd
Original answer
I am looking for a OOP solution / library for Node.js.
You don't need a library. You have ES5.
JavaScript does not have classical OOP. It has prototyping OOP.
This means you have only objects. The only thing you can do with objects is extend, manipulate and clone them.
Manipulate
var o = {};
o.foo = "bar";
Extend
var o = someObject;
Object.defineProperties(o, {
"foo": { value: "foo" },
"bar": { value: "bar" }
"method": { value: function () { } }
}
Clone
var o = someObject;
var p = Object.create(o);
Clone and extend
var o = someObject;
var p = Object.create(o, {
"foo": { value: "foo" },
"bar": { value: "bar" }
"method": { value: function () { } }
}
It's important to understand how Object.create, Object.defineProperty and Object.defineProperties work.
The cloning operation isn't actually cloning. It's creating a new object from a blueprint. A blueprint is an object. It places the blueprint in the [[Prototype]]. The [[Prototype]] lives in the .__proto__ property which I'll use for demonstration.
var o = {};
var p = Object.create(o);
p.__proto__ === o; // true
var q = Object.create(p);
q.__proto__.__proto__ === o;
var r = Object.create(q);
r.__proto__.__proto__.__proto__ === o;
Disclaimer: .__proto__ is deprecated. Don't use it in code. It has it's uses for debugging and sanity checks though.
The main point here is that accessing properties from o in r it has to walk 3 levels up the prototype chain and this gets expensive. To solve that problem, rather then cloning random objects you should clone specific blueprints (and you should have one blueprint per object).
// Parent blueprint
var Parent = (function _Parent() {
// create blank object
var self = Object.create({});
// object logic
return self;
}());
// factory function
var createParent = function _createParent(foo) {
// create a object with a Parent prototype
return Object.create(Parent, {
foo: { value: foo }
});
}
var Child = (function _Child() {
var self = Object.create(Parent);
// other stuff
return self;
}());
var createChild = function _createChild(bar) {
return Object.create(Child, {
bar: { value: bar }
})
};
Here's a snippet from some code I'm working on that you can use as an example:
var Sketchpad = (function _SketchPad() {
var self = Object.create({});
var mousemove = function _mousemove(e) {
this.drawLine(e);
};
self._init = function _init() {
this.$elem.bind({
"mousemove": mousemove.bind(this),
});
this.pens = {};
$("#clear").bind("click", this.clear.bind(this));
$("#undo").bind("click", (function _undoPath() {
this.pen.undo();
}).bind(this));
return this;
};
self.clear = function() {
this.paper.clear();
};
return self;
}());
createSketch = function _createSketchPad(id, w, h) {
var paper = Raphael(id, w, h);
var pen = createPen(paper);
var o = Object.create(Sketchpad, {
paper: { value: paper },
$elem: { value: $("#" + id) },
pen: {
get: function() { return pen; },
set: function(v) { pen = v; }
}
});
return o._init();
};
MooTools is one of the best libraries in terms of OOP Javascript.
You can create classes, interfaces, use inheritance, etc.
Documentation
http://mootools.net/docs/core
Tutorial - MooTools OOP
http://www.phpeveryday.com/articles/MooTools-Basic-Creating-Classes-MooTools-P919.html
You might also be interested in GNU ease.js. If you are not interested in the library itself, its manual goes extensively into the implementation details.
You could also see the author's paper on Classical OOP in ECMAScript.
You could try Joose, https://github.com/SamuraiJack/Task-Joose-NodeJS. Although, I'd personally recommend to stick with Javascript's object functionality as provided by ES5.
In the article you referenced, he was simply giving an example of what was possible with inheritance in javascript. He was not using a framework, but showing you how to extend your own classes you have written.
Frameworks for javascript include Backbone.js (mvc), and MooTools (oop).
extjs has support for OOP with Ext.define and Ext.extend (and Ext.ns). See this example on Sencha.com
Ext.extend is the older method, but is still sometimes useful. You would do something like this:
Ext.ns('myApp.myPackage'); // create a namespace
(function() { // this adds it to the namespace
var MyClass = Ext.extend(BaseClass, {
property: 1,
constructor: function(config) {
Ext.apply(this, config);
},
method: function(a, b) {
this.property = a + b;
}
});
myApp.myPackage.MyClass = MyClass;
}) ()
With Ext.define in Ext 4+ you can do:
Ext.define('myApp.myPackage.MyClass', // don't need to define the namespace first
extend: 'BaseClass' // notice the base class is referenced by a string,
requires: 'AnotherClass',
mixins: { mixin : 'MixinPackage' },
property: 1,
constructor: function(config) {
//...
}
method: function(a, b) {
this.property = a + b;
}
});
Note that you can also use traditional OOP in javascript with 'new' and function.prototype
If you want to do a real strong OOP in Javascript/Node, you can have a look at the full-stack open source framework Danf.
It allows you to use OOP (and so the same classes) on both the server (node) and client (browser) sides.
It also provides a nice dependency injection mechanism (looking as the one of Symfony2 if you come from the PHP community).