What does the dojo.query() return? - dojo

I'm just getting started with dojo, and I've understood that dojo.query is the same as $ in jQuery.
But I haven't figured out what it returns. Is it a specialized object like in jQuery?
What I'm trying to do (with no luck) is:
dojo.query("output").innerHTML = data;
//this doesn't work either:
dojo.query("output").html(data);
//tried accessing by id as well
dojo.query("#output").html(data);
//and tried to access a div, incase dojo has some issues with html5 elements
dojo.query("#divOutput").html(data);
And I'm currently using the new html5 elements:
<output id="output">Output goes here</output>
<div id="divOutput">non-html5 output goes here</div>
And I can't seem to find a good list on what to do with objects returned by dojo.query()..
edit: Okay, I think dojo is just messing with me now. I found this method: addContent() and that works on the above selector. But I don't want to add content, I want to replace content...

The query method returns a NodeList object.
In the ref for NodeList you can find a list of functions that you can apply to the list
of elements. There is no innerHTML function for the list, but the html function should work.
There is no "output" element in HTML, perhaps you try to target elements with the class name "output"?
dojo.query(".output").html(data)
Or the element with id "output"?
dojo.query("#output").html(data)

If you want to replace all the output tags' content with the same thing, then this code should always work:
// replace the contents of ALL <output> tags
dojo.query('output').forEach(function(node) { node.innerHTML = data; });
Dojo also provides a little shortcut for these kinds of things. You can specify a string to NodeList's forEach function like this:
// replace the contents of ALL <output> tags (as long as data is global)
dojo.query('output').forEach("item.innerHTML = data;");
The word item in the string is special. (This is a pain to debug, so it might not be worth it.)

As was said above, query method returns NodeList object, so you can iterate it's result as array, or use dojo methods that work with NodeList (e.g. attr):
dojo.query("#divOutput").attr("innerHTML", data);
But as soon as you are trying to query nodes by id, it would be better to use dojo.byId() method, which returns domNode:
dojo.byId("divOutput").innerHTML = data;
Or in more dojo style:
dojo.attr(dojo.byId("divOutput"), "innerHTML", data)

Try this by adding the [0] like this:
dojo.query("output")[0].innerHTML = data;

Also, there is a dojox.jq wrapper (in development, coming in 1.4) which emulates the JQuery return object APIs

The documentation seems to be a mess, this is the only thing i get to work with 1.7,
dojo.query("whatever").forEach(function(node, index, array)
{
node...
});

Related

How can I create the resource string without a big string?

In After Effects scripts, if you want your script to be able to be docked in the program's workspace, the only way to do it as far as I know is to use a resource string like this:
var res = "Group{orientation:'column', alignment:['fill', 'fill'], alignChildren:['fill', 'fill'],\
group1: Group{orientation:'column', alignment:['fill', ''], alignChildren:['fill', ''],\
button1: Button{text: 'Button'},\
},\
}";
myPanel.grp = myPanel.add(res);
The above code creates a script UI with one button ("button1") inside a group ("group1").
I would like to know other ways to create the same resource string. Is it possible to make it using a JSON object and then stringifying it??
I know it can be done somehow, because I have inspected the Duik Bassel script that is dockable and, for example, adds elements like this:
var button1 = myPal.add( 'button' );
but I cannot understand how to do it myself.
TL;DR: I want to make a dockable scriptUI without writing a giant string all at once, but bit by bit, like a floating script.
UI container elements have an add() method which allows you to add other UI elements to them, and you can treat them as normal objects.
var grp = myPanel.add("group");
grp.orientation = "column";
grp.alignment = ['fill', 'fill'];
grp.alignChildren = ['fill', 'fill'];
var group1 = grp.add("group");
…
var button1 = group1.add("button");
button1.text = 'Button'
More details and examples here: https://extendscript.docsforadobe.dev/user-interface-tools/types-of-controls.html#containers
Also worth checking out https://scriptui.joonas.me/ which is a visual scriptUI interface builder. You have to do some work on the code it produces to get panels for AE, but it's not hard.
extendscript still uses a 20th century version of javaScript, which doesn't have JSON built-in, but I have successfully used a JSON polyfill with it.
I used json2.js to get structured data in and out of Illustrator, and it worked beautifully, but I can see there's now a json3.js which might be better for whatever reason. This stackoverflow question addresses the differences.
To load another .js file (such as a polyfill) into your script, you need to do something like
var scriptsFolder = (new File($.fileName)).parent; // hacky but effective
$.evalFile(scriptsFolder + "/lib/json2.js"); // load JSON polyfill
These file locations may differ in other Adobe apps. Not sure what it would be in AfterEffects. I seem to remember that InDesign has a different location for scripts. You can also hardcode the path, of course.
Good luck!

Materialize select element will not show passed options (empty ul)

My question is probably really easy. I want to populate a Materialize select element with a few options. I don't want to put my options in the HTML, I want to do it from a JSON with javascript (I get the json from a different service). In my HTML file, I just have an empty select.
In the documentation, I see that during the select initalization, you can pass an options object. I assume this can be used to populate the select dropdown. A bit lower it is stated that the correct key is dropdownOptions, and you are supposed to pass an UL to it. I did so, but the UL is empty. Console log shows that the ul is built correctly. What am I missing?
let ul = document.createElement("ul");
arr.forEach(oddelek => {
let li = document.createElement("li");
li.appendChild(document.createTextNode("Four"));
ul.appendChild(li);
});
console.log(ul); // looks good
M.FormSelect.init(selects, {
"dropdownOptions": ul
});
What could be the reason that only an empty ul appears?
Because the above code didn't work, I decided to just add the elements to the HTML with javascript, and then run the init with an empty options object. But it doesn't work, the dropdown is still empty.
The init with empty options object works perfectly if the items are added manually in the html, but does not work if they are added through javascript. I have no idea why, I call the init after I add the elements. Inspecting the resulting html without the init looks just like in the documentation. Any advice?

How to use DataTables fnDestroy method

I am using the DataTables plugin for jQuery but keep getting the following error when I attempt to use the fnDestroy method:
Undefined
I have tried using all of the following variants:
1)
$('#data').dataTable().fnDestroy();
2)
var dt = $('#data').dataTable();
dt.fnDestroy();
3)
var data = document.getElementById('data');
data.fnDestroy();
The 'data' object exists - The HTML is as follows:
<div class="resultset">
<table class="display" id="data">
<tbody>
</tbody>
</table>
</div>
The DataTable is built with Javascript (not shown here), but the base object is hard-coded.
The API documentation shows that the second method should work:
$(document).ready(function() {
// This example is fairly pointless in reality, but shows how fnDestroy can be used
var oTable = $('#example').dataTable();
oTable.fnDestroy();
} );
EDIT
The DataTable displays fine and otherwise works well. The issue arises when I attempt to execute this function.
It seems to be the difference between...
_table = jQuery('table#fp-table-table').dataTable(); // .fnDestroy() works
and
_table = jQuery('table#fp-table-table').DataTable(); // .fnDestroy() doesn't work
DataTable seems to be for API calls back into the object and dataTable seems to be the intialisation method.
In my project I had changed the initialisation to use DataTable instead of dataTable to perform a filtering task. After this my AJAX reloads would throw the 'undefined' error, so I changed it back... i esta.
If you go to http://www.datatables.net/manual/installation and scroll towards the bottom you will see the following:
"Upgrade note:
If you are upgrading from DataTables 1.9 or earlier, you might notice that a capital D is used to initialise the DataTable here. $().DataTable() returns a DataTables API instance, while $().dataTable() will also initialise a DataTable, but returns a jQuery object.
Please refer to the API manual for further information.
Are you sure you are running your fnDestroy within document.ready?
Maybe you are getting the undefined error because the dom is not correctly loaded yet ?
It could also mean your table is incorrect, but I would have to be able to look at that to be sure.
Take a look at this fiddle, which uses your second option, and runs perfectly: http://jsfiddle.net/timsommer/m5sZU/2/
From the datables 1.10 manual:
It is important to note the difference between $( selector ).DataTable() and $( selector ).dataTable(). The former returns a DataTables API instance, while the latter returns a jQueryJS object. An api() method is added to the jQuery object so you can easily access the API, but the jQuery object can be useful for manipulating the table node, as you would with any other jQuery instance (such as using addClass(), etc.).
So you can use DataTable() but then you need to use _table.api().fnDestroy(); it seems.
You can try
table.destroy()
This works in my case

Dojo attach point / byId returns undefined

I made a template and there is a <select dojotype="dijit.form.ComboBox" dojoAttachPoint="selectPageNumber" id="selectPageNumber">tag with id and dojoAttachPoint be "selectPageNumber". I want to populate it with options upon create so I add some code to the postCreate function:
var select = dijit.byId("selectPageNumber");
or
var select = this.selectPageNumber;
but I always have select being undefined.
What am I doing wrong?
UPD:
The problem with element has been solved spontaneously and I didn't got the solution. I used neither dojo.addOnLoad nor widgetsInTemplate : true, it just started to work. But I have found the same problem again: when I added another tag I can't get it!
HTML:
<select class="ctrl2" dojotype="dijit.form.ComboBox" dojoAttachPoint="selectPageNumber" id="selectPageNumber">
</select>
<select class="ctrl2" dojotype="dijit.form.ComboBox" dojoAttachPoint="selectPageNumber2" id="selectPageNumber2">
</select>
widget:
alert(this.selectPageNumber);
alert(this.selectPageNumber2);
first alert shows that this.selectPageNumber is a valid object and the this.selectPageNumber2 is null.
widgetsInTemplate is set to false.
all the code is within dojo.addOnLoad()
dojo.require() is valid
I am using IBM Rational Application Developer (if it is essential).
WHY it is so different?
Based on your syntax, I am assuming that you are using 1.6. Your question mentions template and postCreate, so i am assuming that you have created a widget that acts as a composite (widgets in the template).
Assuming 1.6, in your widget, have you set the widgetsInTemplate property to true. This will tell the parser that your template has widgets that need to be parsed when creating the widget.
http://dojotoolkit.org/documentation/tutorials/1.6/templated/
I would remove the id from the select. Having the id means that you can only instantiate your widget once per page. You should use this.selectPageNumber within your widget to access the select widget.
If you are using 1.7 or greater, instead of setting the widgets widgetsInTemplate property, you should use the dijit._WidgetsInTemplateMixin mixin.
http://dojotoolkit.org/reference-guide/1.8/dijit/_WidgetsInTemplateMixin.html
Depending on when dijit.byId() is being called, the widget may not have been created yet. Try using dojo.addOnLoad()
dojo.addOnLoad(function() {
var select = dijit.byId("selectPageNumber");
});
I came close to the solution: it seems like there is a some sort of RAD "caching" that doesn't respond to changes made in html code.
Ways to purge the workspace environment with RAD (based on Eclipse) might be a solution.

Pass an object to a widget in template

I have a Dojo UI widget that has a widget embedded within it. I need to pass an object to this embedded widget for it to set itself up correctly, but I'm not sure how to do it.
I have been templating in the embedded widget in the template for the wrapper widget, for example:
...<div class="thing"
data-dojo-type="mycompany.widgets.ComplexEmbeddedWidget"
data-dojo-props="stuff: '${stuff}'"></div>...
but this doesn't seem to work, I guess the data is passed as a string maybe?
I'm pulling out this data by setting it to a property in the embedded widget and then referencing it in my postMixInProperties function.
Doubtless this is the wrong approach, what should I be doing to set up an embedded widget such as this?
I think if you are going to use this approach, you want to convert the javascript object json before it is passed to the templated embedded widget.
You can easily do this by requiring 'dojo/json' and doing
this.stuff=jsonModule.stringify(this.stuffAsObject);
As you have already discovered, if you are setting more complex properties, programmatic instantiation is probably the way to go.
If your mycompany.widgets.ComplexEmbeddedWidget is desperate to have the object 'stuff' set allready once it is initialized (in constructor), then im not sure this approach will do, however a simple fix could be removing the ' quotes around ${stuff}?
What happens is basically that you derive the widget with dijit/_TemplatedMixin. This in turn, during buildRendering, calls _stringRepl on 'this' (the widget). I am not completely certain of the flow, since youre working with WidgetsInTemplate..
lets as example, set a widgets attribute to an array via markup:
<div
data-dojo-type="dijit.form.Select"
data-dojo-props="options:[ 'val1', 'val2']">
</div>
As you see, no quotes around the value - or it will render as a string. Lets then change your ComplexEmbedded template to
dojo.declare("exampleName", [_WidgetsInTemplateMixin, _TemplatedMixin], {
templateString: '<div class="outerWidgetDomNode">
...
<div class="thing"
data-dojo-type="mycompany.widgets.ComplexEmbeddedWidget"
data-dojo-props="stuff: ${stuff}"></div>
...
'
});
To instantiate the ComplexEmbeddedWidget.stuff with an object, this needs to be a string. _Templated uses dojo.string.substitute, which probably would fail if given deep nested object.
Markup example:
<div data-dojo-type="exampleName" data-dojo-props="stuff: '{ json:\'Representation\', as:\'String\'}'"></div>
Or via programmatic
var myObj = { obj:'Representation', as:'Object' };
var anExampleName = new exampleName({
stuff: dojo.toJson(myObj) // stringify here
}, 'exampleNode');
Lets know how goes, ive been wanting to look into the presendence of flow with this embedding widgets into template stuff for a while :)
You can programmatically insert widgets. This seems to be be the way to go if the inserted widget requires JavaScript objects to be passed to it.