Adding a custom widget on call back method - dojo

i have a call back method which is called by other class when some event occurs, which send some objects as parameters
loadingIsDone : function(evt)
{
console.log(evt);
for(var i=0; i<evt.layers.length;i++)
{
var row = new _LayerRow(evt.layers[i].layer);
domConstruct.place(row.domNode,this.containerDiv,"last");
}
}
for each object which is received i need to create a Custom widget called _LayerRow, which is having one Checkbox and a Label
When i debug this code the pointer is not coming to the 2nd line of the loop.
and no error in the console..
but when i call the same in different file as below for testing purpose
var obj = new Object();
obj.id = "124";
obj.name = "Chiru";
var lay = new _LayerRow(obj);
domConstruct.place(lay.domNode,win.body(),1);
The _LayerRow widget is working fine
_LayerRow.js
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_OnDijitClickMixin",
"dijit/_TemplatedMixin",
"dojo/text!../templates/_layerrow.html"
], function (declare, _WidgetBase, _OnDijitClickMixin,_TemplatedMixin, template) {
return declare([_WidgetBase,_OnDijitClickMixin, _TemplatedMixin], {
templateString: template,
layer : null,
constructor : function( layerObj)
{
this.id = layerObj.id;
this.layer = layerObj;
},
postCreate : function()
{
this.inherited(arguments);
this.layerName.innerHTML = this.layer.name;
}
});
});
and templates/_layerrow.html
<div>
<input type="checkbox" id="${id}">
<label for="${id}" data-dojo-attach-point="layerName"></label>
</div>
Any idea why its not working.. how can i find the issue in this

Most common reason for callbacks to fail is incorrect scope.
Because in asynchronous callbacks such as above, the context that the code is executing in has changed. It will no longer refer to the object that originally provided it, but its context will now refer to the enclosing object, the callback. To get around this, you can use hitch() to force the function to retain its original context
I see a reference to this in your callback, use hitch to correct the scope.
More info: http://dojotoolkit.org/reference-guide/1.9/dojo/_base/lang.html#hitch

Related

startup is not getting called for Dojo Custom Widget

I created a Custom Widget in Dojo
return declare("DrawTools", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
templateString: template,
layers: [],
constructor: function (featureLayerArr) {
},
postCreate: function () {
},
startup: function () {
var menu = new DropDownMenu({ style: "display: none;" });
var menuItem1 = new MenuItem({
label: "Save",
iconClass: "dijitEditorIcon dijitEditorIconSave",
onClick: function () { alert('save'); }
});
menu.addChild(menuItem1);
var menuItem2 = new MenuItem({
label: "Cut",
iconClass: "dijitEditorIcon dijitEditorIconCut",
onClick: function () { alert('cut'); }
});
menu.addChild(menuItem2);
menu.startup();
var button = new DropDownButton({
label: "hello!",
name: "programmatic2",
dropDown: menu,
id: "progButton"
}, this.drawToolsMenuNode).startup();
},
startMenu: function () {
}
});
Wdiget template is as follows
<div>
<div data-dojo-attach-point="drawToolsMenuNode"></div>
</div>
I am instantiating Widget in another Custom Widget as follows
var drawTools = new DrawTools(this.allLayersArr);
drawTools.placeAt(this.drawToolsNode);
drawTools.startMenu();
startup method for DrawTools widget is not getting called.
Need help in this regard.
Offical definition from dojo
startup():
Probably the second-most important method in the Dijit lifecycle is the startup method. This method is designed to handle processing after any DOM fragments have been actually added to the document; it is not fired until after any potential child widgets have been created and started as well. This is especially useful for composite widgets and layout widgets.
When instantiating a widget programmatically, always call the widget's startup() method after placing it in the document. It's a common error to create widgets programmatically and then forget to call startup, leaving you scratching your head as to why your widget isn't showing up properly.
So as Kirill mentioned, you need to call the startup method.
The alternative solution would be moving widget instantiation logic from ::startup() to ::postCreate(), since ::postCreate() will be called for sure.

How to use get (getter) keyword in dojo

I am looking for a functionality like below in dojo class (AMD).
function () {
var _foo = 123; // private variable
return {
get foo () { return _foo; }
};
}());
The advantage is once the value _foo is initialised user can't modify the value _foo or foo from the console. How do we achieve this in dojo class like
define["dojo/_base/declare", "dojo/request", "dojo/_base/lang"],
function(declare, request, lang) {
var _foo = 123;
return declare("fooClass", null, {
get foo () { return _foo; }
});
});
when i declare as above when we try to build the class using dojo build.sh it is giving parsing errors.how to make a value not changeable from debug console in dojo
There is not really an advantage with your solution, as if an user open the debugger console, a developer could set a breakpoints at var _foo =123; and change its value at run time any time.
In your code actually you are showing an example of a closure.
A closure allow your var _foo to be hidden inside your function() so a developer would not be able to get that value within its js code.
Nevertheless you can still access it using the debugger console adding a simple breakpoint.
If you are interesting to use getter and setter for dojo, please read on.
The dojo naming convention to specify a custom getter or setter (in this example for an attribute named foo) is:
_setFooAttr() and _getFooAttr()
Example:
require([
"dojo/_base/declare", "dojo/dom-style", "dojo/parser", "dojo/ready",
"dijit/_WidgetBase", "dijit/_TemplatedMixin"
], function(declare, domStyle, parser, ready, _WidgetBase, _TemplatedMixin){
declare("HidePane", [_WidgetBase], {
// parameters
open: true,
// custom setter
_setOpenAttr: function(/*Boolean*/ open){
this._set("open", open);
domStyle.set(this.domNode, "display", open ? "block" : "none");
}
});
ready(function(){
parser.parse();
});
});

Testing a ReactiveVar in Meteor

I am trying to test a event in a Template. Within the click event, there is a Reactive Var that gets set.
The test is blocked by the Reactive Var.
Any ideas on how I stub or mock a reactive var in a template? I tried adding it to the template (template.reactiveVar = "whatever"), but no go, as I don't think it's writeable.
The ?? indicate where I am wondering what to put.
it('sets a reactive var when a button is clicked', function(){
Template.button.fireEvent('click ', {
context: { some: 'values'},
event: {'target': {'text': 'randomText' } },
templateInstance:new Template('upload', function(e) {
this.reactiveVar = new ReactiveVar("not clicked)
})
});
chai.assert.equal(Template.reactiveVar, "clicked")});
// The actual template is below..
Template.button.events{(
'click button': function(evt, template){
var text = evt.target.text
template.reactiveVar.set(text)
}
});
The result is...
TypeError: Cannot read property 'set' of undefined
Any ideas?
I suspect the event is actually firing and getting through to your event handler before the callback that creates the reactive var has run.
You should create the reactive var when you first create the template independent of your test:
Template.button.onCreated(function(){
this.reactiveVar = new ReactiveVar("not clicked");
});

Dojo _TemplatedMixin: changing templateString

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

trying to derive from a dijit Button: "TypeError: this._attachEvents is undefined"

I am trying to derive an "extended" Button from dijit/Form/Button. (I want to pass extra arguments to the constructor, and I want to encapsulate these preparations in my derived class. Button is just an example, I want to use this later with grids and trees.)
Unfortunately the code below fails with "TypeError: this._attachEvents is undefined" in the firefox javascript console. Some idea, what is wrong? The same code, including minimal HTML, is ready to run at http://jsfiddle.net/x9dLs8gz/1/
require(["dojo/_base/declare", "dijit/form/Button", "dojo/dom", "dojo/json", "dojo/domReady!"],
function (declare, Button, dom, json) {
declare("MyButton", Button, {
"-chains-": {
constructor: "manual"
},
constructor: function () {
//extra calculation will go here...
this.inherited(arguments);
}
});
new MyButton({
label: "Click Me!",
onClick: function () {
dom.byId("result").innerHTML += "Success";
}
}, "button").startup();
});
Cheers,
Dominic
If the "-chains-" value for the constructor method is either not set or set to "after", then the postscript method will be called after all of the inherited constructors have been fired. On the other hand, when specifying "manual", postscript is fired after the first constructor (in this case MyButton#constructor) is executed. As a result, _AttachMixins#buildRendering is fired before this._attachEvents has been set in _AttachMixins#constructor, causing the error you see.
Since specifying "manual" means that no chaining is assumed whatsoever, mixin contructors will never be called, even if this.inherited is correctly called up the chain. This makes sense, as the underlying C3MRO is thrown out the window.
If you need to continue using the "manual" setting in spite of this, you will need to 1) recreate any missing data yourself, 2) manually call the mixin constructors (e.g., _AttachMixin.prototype.constructor.call(this)), or 3) convert MyButton to a factory for Button:
var createButton = (function () {
var myButtonDefaults = { ... };
return function (kwArgs, id) {
var buttonId = id || 'button';
return new Button(lang.mixin({}, myButtonDefaults, kwArgs), buttonId);
};
})();
var myButton = createButton();
myButton.startup();
console.log(myButton instanceof Button); // true
You need to assign the declared class to a variable or declare in a different file and added it to the list of object in the require. Also dont use variable names which are keywords like "constructor". below is the fixed version of your example.
require(["dojo/_base/declare", "dijit/form/Button", "dojo/dom", "dojo/json", "dojo/domReady!"],
function(declare, Button, dom, json) {
var MyButton = declare("MyButton", Button, {
"-chains-": {
constructorType: "manual"
},
constructor: function() {
//extra calculation will go here...
this.inherited(arguments);
}
});
new MyButton({
label: "Click Me!",
onClick: function() {
dom.byId("result1").innerHTML += "Success";
}
}, "button").startup();
});
<div id="button"></div>
<div id="result1"></div>