Dojo and unregistering widgets - dojo

I am new to the Dojo Toolkit. I'm getting the error
Tried to register widget with id=myButton but that id is already registered
whenever I try to load dojo content twice (meaning I load HTML content through jQuery.Load into a container div). Is there a way of unregistering already registered widgets in dojo? I've seen some examples, but I don't really get them working.
My button:
<button dojoType="dijit.form.Button" id="myButton">button</button>

If you're looking to unregister specific widgets, you can use their destroy() or destroyRecursive() methods. The second one destroys any widgets inside the one you are destroying (i.e. calling destroyRecursive on a form widget will also destroy all the form components).
In your case, it sounds like your best bet would be to do this before jQuery.load -
var widgets = dijit.findWidgets(<containerDiv>);
dojo.forEach(widgets, function(w) {
w.destroyRecursive(true);
});
The above code will unregister all widgets in <containerDiv>, and preserve their associated DOM Nodes. To destroy the DOM nodes, pass false to destroyRecursive instead.
Reference:
http://dojotoolkit.org/api/1.3/dijit/_Widget/destroyRecursive

Based on http://bugs.dojotoolkit.org/ticket/5438, I found a sufficient way of destroying dojo-widgets:
dijit.registry.forEach(function(w){
w.destroy();
});

This worked for me:
dijit.byId( 'myButton' ).destroy( true );

I think you would be better off removing the id from your button and accessing it using an attach point. You would basically do <button dojoType="dijit.form.Button" data-dojo-attach-point="myButton">button</button>
then in your code you would access it like this.myButton.... however im not sure which version of dojo you are using. This will fix any id issues since dojo will assign a unique id to it automatically.

Related

Aurelia - dynamically create custom element in a view-model

I have an Aurelia app where a user can click on a button and create a new tab. The tab and its content (a custom element) do not exist on the page before the user clicks the button. I am generating the HTML for the content at runtime (via Javascript) in my view model.
I keep seeing mention of using the template engine's compose or enhance functions, but neither are working for me. I don't know how I would use the <compose> element (in my HTML) since I am creating the element based on the user clicking a button.
My thought was that the button has a click.delegate to a function that does ultimately does something like
const customElement = document.createElement('custom-element');
parentElement.appendChild(customElement);
const view = this.templatingEngine.enhance({
element : customElement,
container : this.container, // injected
resources : this.viewResources, // injected
bindingContext: {
attrOne: this.foo,
attrTwo: this.bar,
}
});
view.attached();
But all this does is create an HTML element <custom-element></custom-element> without actually binding any attributes to it.
How can I create a custom element analogous to <custom-element attr-one.bind="foo" attr-two.bind="bar"></custom-element> but via Javascript?
As you pointed out in your own answer, it's the missing resources that caused the issue. One solution is to register it globally. That is not always the desired behavior though, as sometimes you want to lazily load the resources and enhance some lazy piece of HTML. Enhance API accepts an option for the resources that you want to compile the view with. So you can do this:
.enhance({
resources: new ViewResources(myGlobalResources) // alter the view resources here
})
for the view resources, if you want to get it from a particular custom element, you can hook into the created lifecycle and get it, or you can inject the container and retrieve it via container.get(ViewResources)
I found the problem =\ I had to make my custom element a global resource.

Default-expand-all doesn't work for q-tree? Vue.Js

I have to code a web application and the most important element is the q-tree. I'm already able to load and show data (passing an array called list), but I want that all nodes are expanded.
The vue.js examples of the official documentation show that you're be able to do this with the 'default-expand-all' attribute but this isn't working for me.
It only shows me the root node with an arrow, where I have to expand the children nodes manually.
<q-tree
:nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
default-expand-all
></q-tree>
Taking a cue from the accepted answer, I realised that the dom has already been created with the tree component on first render.
In my use case, I want to update the Tree when data comes back from the server.
So, I had to force it to re-render with the expanded functionality using:
this.$nextTick(function () {
this.$refs.nodes.expandAll();
})
The nextTick function will update the dom in the next window of execution, by which time the nodes will get expanded by calling the expandAll function.
And NB: For those confused by the astericks on the ref attribute or how to add it to the component, here goes:
<q-tree :nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
ref="nodes"
>
Solved my problem as following:
I have added a ref attribute to the QTree DOM Element which makes it possible to access predefined methods of QTree API.
<q-tree
:nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
**ref="nodes"**
>
The function I have been using is expandAll().
updated() {
this.$refs.nodes.expandAll();
}
The most important thing for me was, I had to find out which lifecycle hook was the right one for me. The update() hook was the one I was looking for.
The reason:
Called after a data change causes the virtual DOM to be re-rendered and
patched.
The component’s DOM will have been updated when this hook is called, so you
can perform DOM-dependent operations here.
The default-expand-all is only applied on the first rendering of that Component.
So if your Component renders when the nodes aren't assigned they wont expand if assigned afterwards.
https://v1.quasar-framework.org/vue-components/tree
You have to work with scoped slots and an expanded attribute if you dont have the nodes on first rendering.

Initialize dynamic Component in Code using Vue.js

I am currently developing a web application that is used to display elements for events on a map provided by HERE Maps. I am using Vue.
I have some components, but the relevant component is the component HereMaps.vue which initializes the map using the HERE Maps Api.
The HERE Maps Api provides the possibility to place so called InfoBubbles on the map showing additional information. These InfoBubbles can be provided some HTML-code in order to customize their appearance.
Please refer to the documentation for additional information
Following the documentation the code looks something like this:
let bubble = new H.ui.InfoBubble(marker.getPosition(), {
content: "<div class='someClass'>Some Content</div>"
});
this.ui.addBubble(bubble)
This is happening after mount in the "mounted" method from Vue in the "HereMaps" component.
The Bubbles are added in a "closed" (hidden) form and dynamically "opened" to reveal their content when the corresponding marker icon on the map is clicked. Therefore the HTML-code is present on the DOM after the component is mounted and is not removed at a later stage.
Now instead of supplying custom code within each bubble added to the UI i want to just add a component like this:
let bubble = new H.ui.InfoBubble(marker.getPosition(), {
content: "<myDynamicComponent></myDynamicComponent>"
});
this.ui.addBubble(bubble)
It does not matter to me wether the component is initialized using props or if it is conditionally rendered depending on the state of a global variable. I just want to be able to use the "myDynamicComponent" in order to customize the appearance in a different file. Otherwise the design process gets very messy.
As far as i know this is not possible or at least i was not able to get it work. This is probably due to the fact that the "myDynamicComponent" is not used within the "template" of the "HereMaps" component und thus Vue does not know that it needs to render something here after the directive is added to the DOM in the "mounted" method.
This is what the InfoBubble looks using normal HTML as an argument:
This is what the InfoBubble looks using the component as an argument:
It appears to just be empty. No content of the "myDynamicComponent" is shown.
Does anyone have any idea how i could solve this problem.
Thank You.
Answer is a bit complicated and I bet you wouldn't like it:)
content param can accept String or Node value. So you can make new Vue with rendered your component and pass root element as content param.
BTW, Vue does not work as you think, <myDynamicComponent></myDynamicComponent> bindings, etc exists in HTML only in compile time. After that all custom elements(components) are compiled to render functions. So you can't use your components in that way.
Give us fiddle with your problem, so we can provide working example:)

Vuejs directive masonry detect prepend to array and redraw properly

I am using the vue-masonry plugin which let me create a masonry grid easily.
I created a system of infinite loading where you scroll to the bottom of the page and it append new pictures to an array binded with the vue-masonry plugin.
The problem happen when I created a system of polling for the new pictures that were upoaded by other users. Those new pictures need to be at the top of the masonry grid.
The plugin use two Vue Directive masonry (parent) and masonryTile (element). masonryTile has a v-for which loop through the array binded with my Vue instance (which does all the heavy lifting, preloading, sanityzing, etc...).
Is there a way in the directives to know the differences between something being appended or prepended? And try to react differently (I know masonry has some append/prepend method) but in here and with this plugin, the items where already added (at the beginning so the prepend works with Vue) but there's no masonry interaction nor redraw (I tried to use the prototype to trigger the redraw this.$redrawVueMasonry();).
So I don't know what's next to do. Continue finding a way to differentiate a prepend from a append and trying to bind it to the respective masonry's methods ? Or another method that I didn't think of...
Thanks in advance for you help
Ps : I don't think my code is really relevant since It's more a way to optimize the plugin. If you want some specific part of my code anymay, tell me in the comment !
This probably comes a bit too late, this being a 10 month old question.
However vue-masonry is able to handle situations where items are spliced anywhere in the array. But to properly update the grid this.$redrawVueMasonry() should be called inside this.$nextTick() like this:
this.$nextTick(() => this.$redrawVueMasonry());
Hope this helps, if not the original poster, someone else.

dijit.Menu to be displayed after DOM had been set up

I have set up in Javascript my preferred dijit.Menu which is that far so good.
How am I able to display the dijit.Menu directly after the page starts up in the (with it's position) without any mouse interaction?! I have looked in the API so far and don't find the answers. Will I have to "overwrite" a method?
If yes, which one is it? And what do I have todo???
The widget will not show up until it is parsed by dojo.
You should place fake menu markup inside its dom node:
<div dojoType="dijit.Menu">
<h1>This text is shown after the dom is loaded
and until the menu is parsed and renered</h1>
</div>
As soon as menu is ready, everything you've placed inside menu's dom node will be replaced by actual widget's html.
NON-AMD Version:
dojo.ready(function(){
// The code for all items!
})
DOJO-AMD Version, put the parameters of the modules you like to add, in require as well give them a name in the functions parameters list:
require(["dojo/domReady!"],function(){
//natve code
});