Nested dojo widgets not working - dojo

I have created a very simple templated widget. Something like this:
function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, ..., ...) {
return declare("widgets.some.Widget", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {..
...
templateString: template,
widgetsInTemplate: true,
...
}
This widgets loads and works just fine. However, when I want to use it in another templated widget like this:
<div style="width: 400px" data-dojo-type="widgets.some.Widget" ... >
</div>
.. I end up with 2 errors:
Cannot read property 'nodeType' of null"
and
widgets.somewidgetusing.SomeWidget: parser returned unfilled promise
(probably waiting for module auto-load), unsupported by
_WidgetsInTemplateMixin. Must pre-load all supporting widgets before instantiation."
Both widgets works great stand alone, but when I use widgets.some.Widget in another widget the errors occur.

I was getting this same message. The culprit turned out to be non-unique html ids. I was nesting some.Widget inside of two different widgets. Some.Widget's template html has a textbox with id="Text1". I removed the id and problem resolved.

Related

Dojo1.9: Custom widget inherited from another custom widget template string update not reflected

I have a custom widget which extends _WidgetBase, _TemplatedMixin with template
<div dojoAttachPoint="widget">
<div dojoAttachPoint="title">${name}</div>
<div dojoAttachPoint="dnmschart"></div>
</div>
and another widget which extends above widget
require([
'dojo/_base/declare',
'my/widget/view/AbstractWidget'
], function (declare, AbstractWidget) {
return declare("my.widget.view.AbstractChart", [AbstractWidget], {
constructor:function(){
},
buildRendering: function(){
this.inherited(arguments);
var gridDiv = document.createElement("div");
gridDiv.setAttribute("dojoAttachPoint", "gridPlaceHolder");
},
postCreate:function(){
this.inherited(arguments);
//Here I could not get newly created node gridPlaceHolder
console.log(" POST CREATION");
}
});
});
When I print in console (Break point in post create method)
this.domNode
It shows newly created node at last in document(last node in above template)
<div dojoattachpoint="gridPlaceHolder"></div>
But I could not access gridPlaceHolder attach point in post create method.
Is there anything else need to configure?
Please help me on this:)
data-dojo-attach-point (which you should use for 1.6+ instead of dojoAttachPoint) allows you to have handles for dom nodes in your template.. It is parsed by _TemplatedMixin's buildRendering(), so it will be available in your buildRendering method just after this.inherited line.
You can not set data-dojo-attach-point using setAttribute, it can only be defined in templates to be parsed by TemplatedMixin. If you need your child widget to add some markup in addition to what there is in its parent's template, you can define a variable in your parent's markup, and overwrite it in your child widget:
Your AbstractWidget template:
<div data-dojo-attach-point="widget">
<div data-dojo-attach-point="title">${name}</div>
<div data-dojo-attach-point="dnmschart"></div>
${childMarkup}
</div>
And then you need to add your additional markup in child's buildRendering, before this.inherited:
require([
'dojo/_base/declare',
'my/widget/view/AbstractWidget'
], function (declare, AbstractWidget) {
return declare("my.widget.view.AbstractChart", [AbstractWidget], {
buildRendering: function(){
this.childMarkup = '<div data-dojo-attach-point="gridPlaceHolder"></div>';
this.inherited(arguments);
}
});
As stafamus said, the primary problem here is that you're attempting to assign data-dojo-attach-point or dojoAttachPoint to a node after the template has already been parsed (which happens during the this.inherited call in your buildRendering.
Going beyond that, given the code in the original post, it also appears you're never actually adding the markup you create in buildRendering to your widget's DOM at all - you've only created a div that is not attached to any existing DOM tree. I'm a bit confused on this point though, since you claim that you are seeing the markup for the node you added, which shouldn't be possible with the code above, or the code in stafamus' answer.
Rather than attempting to dump extra markup into your template, you might as well do the programmatic equivalent to what an attach point would be doing in this case anyway: create your DOM node, attach it to your widget's DOM, and assign it to this.gridPlaceHolder. e.g.:
buildRendering: function () {
this.inherited(arguments);
this.gridPlaceHolder = document.createElement('div');
this.domNode.appendChild(this.gridPlaceholder);
}

How to parse a Dojo custom widget

I'm having some trouble with writing my own widget. I've created a widget that inherits from _WidgetBase, _TemplatedMixin and _WidgetsInTemplateMixin (as I'm also trying to build a UI where the widgets will contain other widgets). I'm loading the markup for the widget from a template using dojo/text. Here is a snippet:
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojo/text!./templates/LayerManagerWidget.html"
], function (declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, template) {
return declare("myApp.ui.platforms.desktop.widgets.LayerManagerWidget", [_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
// summary:
// This widget provides the layer manager user interface component
// templateString: string
// Inherited from _TemplatedMixin, used to specify an HTML file to provide the markup
templateString: template
});
});
It's working to some degree and I think the problem is with parsing. Because the widgets aren't all required right from the start, I don't think they are being parsed. Some of the examples on the dojo widget tutorials include dojo/ready and dojo/parser in the widget and parse it but I haven't been able to work out how to do this: those same examples don't have a 'return' before the declare statement which I don't quite understand as then how do you then later instantiate the widget with something like var myWidget = new MyWidget(); ?
So I guess the main part of the question is, in the above example, where do I put a parser.parse() call (or in general, how do I make sure this widget gets parsed)?
Thanks!
In the widget code itself you shouldn't put stuff like parser.parse() or anything similar. To make sure a widget is being parsed, you have to import it in your main JavaScript file (one with a require() block) and parse it either automatically or manually.
So for example, if the widget is located in a file called MyWidget.js, you put something like this in your main JavaScript file (for example app.js):
require([ "custom/MyWidget", "dojo/parser" ]);
This will work just fine if you have configured Dojo to parse automatically. If you rather want to parse manually, you could use:
require([ "dojo/parser", "custom/MyWidget", "dojo/domReady!" ], function(parser) {
parser.parse(); // Parses the entire DOM
});
If you want to programmatically create an instance of your widget, you don't need to parse at all, you just use:
require([ "custom/MyWidget", "dojo/domReady!" ], function(MyWidget) {
var myWidget = new MyWidget({ }, "myDomNodeId");
});
For example: http://plnkr.co/edit/K3Zln6Q00g6v2DQeWcIM?p=preview
Did you read
http://dojotoolkit.org/reference-guide/1.9/quickstart/writingWidgets.html
->
ready(function(){
// Call the parser manually so it runs after our widget is defined, and page has finished loading
parser.parse();
});
Since you write a custom dijit widget, you can read about other functions in its lifecycle here:
http://dojotoolkit.org/documentation/tutorials/1.8/understanding_widgetbase/
-> "postCreate", "startup"

How can I hide a dijit/form/button?

I think it is a common sense that providing a simple way to hide/show and enable/disable a button, but I cannot find any document that describe dojo has done such thing.
Any way, I hope it is my fault that I have missed out something while googling, thanks!
The following coding is what I have tried but they just make the button's text invisible:
dojo.style(btnInsert, {'visibility':'hidden'});
dojo.style(btnInsert, {'display':'none'});
UPDATE Question:
To oborden2:
I have tried your code, the result is same as the above code, here is the captured screen:
To MiBrock:
I have also tried your code and also get the result that same as the above code:
Form widgets in Dijit are special. For all normal Dijit widgets, the domNode (outermost node) of the widget receives the id property. However, with form widgets, the focusNode (which corresponds to the <input> element) receives the ID instead, so that things like <label for="foo"> work properly. In this case, the outermost node has no ID, and you’re actually just hiding the inner HTML input element.
If you already have reference to the widget:
require([ 'dojo/dom-style' ], function (domStyle) {
domStyle.set(widget.domNode, 'display', 'none');
});
If you only have a reference to the ID of the widget/original DOM node:
require([ 'dojo/dom-style', 'dijit/registry' ], function (domStyle, registry) {
domStyle.set(registry.byId(nodeId).domNode, 'display', 'none');
});
Try
require(["dojo/dom-style","dojo/domReady!"], function(domStyle){
domStyle.set(dojo.byId(domNode),'display','none');
});
The variable "domNode" stays for the id of the Node that should be influenced. This is the way we make it.
Regards, Miriam
Try using the Toggler module
require(["dojo/fx/Toggler"], function(Toggler),{
// Create a new Toggler with default options
var toggler = new Toggler({
node: "btnInsert"
});
// Hide the node
toggler.hide();
// Show the node
toggler.show();
});
http://dojotoolkit.org/reference-guide/1.9/dojo/fx/Toggler.html
I imagine you would want to link this to some event using Dojo's on module. Link it up to whatever condition triggers the button's need to be hidden.

Dojo declarative vs. programmatic creation of Select elements with stores

I'm trying to hook up a Select element with a Dojo store. The Select element is declared in HTML and I'm trying to give it a store in some JavaScript code.
It seems the Dojo documentation recommends against this and is in favor of programatically creating the Select element when using a store. However this is a yellow flag to me because I like to keep creation of HTML elements separate from their behavior. In this case, it would be ideal if I could keep the Select element in HTML and hook up the store in JavaScript.
Is the statement in the Dojo docs really the 'best practice' for this? I'm looking for opinions from experienced Dojo developers as I'm still getting my feet wet with Dojo.
Intuitively one would use select.set("store", store) to assign/change store to a dijit as all widgets are dojo/Stateful, but surprisingly it does not work.
Anyway there is a method select.setStore(store, selectedValue, fetchArgs) which (also surprisingly) is not deprecated and works.
Define dijit/form/Select without a store:
<select id="select1" data-dojo-type="dijit/form/Select"></select>​
Assign a store to it:
require([
"dojo/ready",
"dijit/registry",
"dojo/store/Memory",
], function(
ready, registry, Memory
) {
ready(function() {
var store1 = new Memory({
idProperty: "value",
data: [
{ value: "AL", label: "Alabama" },
{ value: "AK", label: "Alaska" },
{ value: "AZ", label: "Arizona" }
]
});
var select1 = registry.byId("select1");
select1.set("labelAttr", "label");
select1.setStore(store1, "AZ");
});
});
See it in action at jsFiddle: http://jsfiddle.net/phusick/ZmsYV/
Adding some UX sugar to the aforementioned I would create dijit/form/Select disabled with single option e.g. Loading... and its final desired width:
<select
id="select1"
data-dojo-type="dijit/form/Select"
data-dojo-props="disabled:true"
style="width:150px;"
>
<option>Loading...</option>
</select>​
Then I would enable it after calling setStore():
var select1 = registry.byId("select1");
select1.set("labelAttr", "label");
select1.setStore(store1);
select1.set("disabled", false);
See this enhanced version at work: http://jsfiddle.net/phusick/xdDEm/
Debugging bad store data/definitions can get pretty nasty when doing so declaratively. Additionally, you may run into strange annoyance when trying to create multiple of the same widget following a declaratively built select/store combination. For example (pseudocode):
<div dojotype="dojox.data.QueryReadStore" url="someurl/blah.do" jsId="mystore"/>
<select dojotype="dijit.form.FilteringSelect" store="mystore">
</select>
The would in theory do what you want by binding mystore to the select, however if you were to create multiple of this widget, you'd have an id conflict with "mystore." As a workaround you'd have to do something like jsId="${id}_mystore" for both the jsId and the store's id.
One option if you would like to keep a declarative behavior is to have attachpoints for both your store and your select, then you can simply call selectwidget.set("store",mystore) after initialization.

Difference between dojoAttachpoint and id

<div dojoType="dojo.Dialog" id="alarmCatDialog" bgColor="#FFFFFF" bgOpacity="0.4" toggle="standard">
<div class='dijitInline'>
<input type='input' class='dateWidgetInput' dojoAttachPoint='numberOfDateNode' selected="true">
</div>
how to show this dialog I tried dijit.byId('alarmCatDialog').show();
The above code is a template and I called dijit.byId('alarmCatDialog').show() from the .js file .
dojo.attr(this.numberOfDateNode) this code works and I got the data .but if I change dojoattachpoint to id then I try dijit.byId('numberOfDateNode') will not work;
Your numberOfDateNode is a plain DOM node, not a widget/dijit, i.e. javascript object extending dijit/_Widget, which is the reason you cannot get a reference to it via dijit.byId("numberOfDateNode"). Use dojo.byId("numberOfDateNode") instead and you are all set.
dojoAttachPoint or its HTML5 valid version data-dojo-attach-point is being used inside a dijit template to attach a reference to DOM node or child dijit to dijit javascript object, which is the reason dijit.byId('alarmCatDialog').numberOfDateNode has a reference to your <input type='input' class='dateWidgetInput' .../>.
The main reason to use data-dojo-attach-point is that:
you can create multiple instances of dijit and therefore your template cannot identify nodes/dijits by IDs as you will have multiple nodes/dijits with the same ID
it's an elegant declarative way, so your code won't be full of dijit.byId/dojo.byId.
It is important to keep track of what is the contents and which is the template of the dijit.Dialog. Once you set contents of a dialog, its markup is parsed - but not in a manner, such that the TemplatedMixin is applied to the content-markup-declared-widgets.
To successfully implement a template, you would need something similar to the following code, note that I've commented where attachPoints kicks in.
This SitePen blog renders nice info on the subject
define(
[
"dojo/declare",
"dojo/_base/lang",
"dijit/_Templated",
"dijit/_Widget",
"dijit/Dialog"
], function(
declare,
lang,
_Templated,
_Widget,
Dialog
) {
return declare("my.Dialog", [Dialog, _Templated], {
// set any widget (Dialog construct) default parameters here
toggle: 'standard',
// render the dijit over a specific template
// you should be aware, that once this templateString is overloaded,
// then the one within Dialog is not rendered
templateString: '<div bgColor="#FFFFFF" bgOpacity="0.4">' +// our domNode reference
'<div class="dijitInline">' +
// setting a dojoAttachPoint makes it referencable from within widget by this attribute's value
' <input type="input" class="dateWidgetInput" dojoAttachPoint="numberOfDateNode" selected="true">' +
'</div>' +
'</div>',
constructor: function(args, srcNodeRef) {
args = args || {} // assert, we must mixin minimum an empty object
lang.mixin(this, args);
},
postCreate: function() {
// with most overrides, preferred way is to call super functionality first
this.inherited(arguments);
// here we can manipulate the contents of our widget,
// template parser _has run from this point forward
var input = this.numberOfDateNode;
// say we want to perform something on the numberOfDateNode, do so
},
// say we want to use dojo.Stateful pattern such that a call like
// myDialogInstance.set("dateValue", 1234)
// will automatically set the input.value, do as follows
_setDateValueAttr: function(val) {
// NB: USING dojoAttachPoint REFERENCE
this.numberOfDateNode.value = val;
},
// and in turn we must set up the getter
_getDateValueAttr: function() {
// NB: USING dojoAttachPoint REFERENCE
return this.numberOfDateNode.value;
}
});
});