AnsweredAssumed Answered

Secrets of the Data-Dojo-Attach-Point (when building custom widgets)

Question asked by agelfert on Feb 5, 2020
Latest reply on Feb 5, 2020 by agelfert

The title of this thread may be confounding to someone who either has either never heard of dojo attach or else has long ago mastered the science of dojo. I have been trying to wrap my brain around some of it, and am forking this from a separate thread. Thanks for Robert Scheitlin, GISP and Ken Buja for chiming in there.

 

Full disclosure: I'm working within the confines of widget.cs/widget.html trying to build custom widgets for WAB. So I may be falsely attributing behavior or issues to Dojo that are actually rooted in some of the jimu overhead that I'm inheriting.

 

Say, you have a super simple widget.html that looks like this:

<div id="main" data-dojo-attach-point="main">

(The following code snippets come from my startup function in widget.js. )

I thought i would be able to create a new div inside "main" as follows:

var existingDiv = document.getElementById("main");
var newDiv = document.createElement("div");
existingDiv.appendChild(newDiv);
var newStuff = document.createTextNode("Such a pretty Div");
newDiv.appendChild(newStuff); 

But that doesn't work.

TypeError: Cannot read property 'appendChild' of null

So, regardless of what 'id' I give my element, it gets assigned an id that looks something like this:

widgets_MyNewWidget_Widget_19

Wow, once I figured that out and used the above as id, appendChile works just fine. Much simpler even using the dojo/dom-construct and the data-dojo-attach-point is to do this:

domConstruct.place("<div>dojo created div</div>",this.main,"last")

Now, I want to create something a little more useful than just a text div. I want to have a dropdown to select from.

 

define([...,"dijit/form/Select",...],
function(...,Select, ...)
...

var mySelect = new Select({
  name: 'myselect',
  dropDown: true,
  options : [
   {label: 'Bernie',value: "Option1"},
   {label: 'Buttie',value: "Option2"},
   {label: 'Bidey',value: "Option3"}
   ]
},<needs some id here>);

Oh, wait now, my new <dojo-created-div> needs an id to know where to go.

  • If I use "main", nothing happens. I don't see my select.
  • if I use the "widgets_MyNewWidget..." as 'id', I get an error:
Tried to register widget with id==widgets_MyNewWidget_Widget_19 but that id is already registered

So instead, I add a new 'id' to my domConstruct:

domConstruct.place("<div>id='newdiv' dojo created div</div>",this.main,"last")

Now, I can use the "newdiv" id inside the definition of the new Select, and it gets created.

 

Now, here is the problem I haven't figured out how to solve.  On my event listeners, previously when the select would have been set up as <select> in HTML, I have been using something like:

on(this.myselect,"change",lang.hitch(this, function(evt){
console.log("select has changed);
}));

... where again, 'myselect' would be a reference to a dojo-attach points. So now I am trying to figure out if there is a way to create the attach point programmatically. I've tried adding it to the domConstruct.place():

domConstruct.place("<div data-dojo-attach-point='myselect' id='newdiv'></div>",this.main,"last"); 

That doesn't work. I've also tried the following once the Select has been created:

mySelect.set("data-dojo-attach-point","myselect");// - doesnt' work

 

That doesn't work either, likely because ...

set() and get()

In general attributes can be both set at initialization and modified after the widget is created, although some attributes, like “id” and “type”, which are marked [const]... (from  Dojo documentation)

So is there a way to do this in javascript? Or do I have to set this up in my HTML? And if so, how do I do it?

 

Every time I create another HTML element to contain my programmatically created Select, the Select shows up but I can't reference it using "this.myselect".

What am I doing wrong? What's the best practice that get the best of both worlds - hrml and javascript - in this case?

Outcomes