Dojo loading templated widget into data-dojo-type - dojo

I've created a very simple HTML templated widget.
<div class="tool">
<a href="" id="freehand">
<i class="flaticon-writing9"></i>
</a>
</div>
<div class="tool">
<a href="" id="polygon">
<i class="flaticon-constellation"></i>
</a>
</div>
My widget declaration looks like this:
define([
"dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dojo/text!./templates/DrawingToolsWidget.html",
"dojo/parser",
"dojo/ready"
],
function(declare, _WidgetBase, _TemplatedMixin, template, parser, ready) {
return declare("DrawingToolsWidget",[_WidgetBase, _TemplatedMixin], {
templateString: template,
constructor: function() {
console.log(this);
}
});
ready(function(){
parser.parse();
});
}
);
I try to attach it to a point in my main DOM with a data-dojo-type attribute:
<div id="drawing-tools-container" data-dojo-type="DrawingToolsWidget"></div>
But I keep getting a generic dojo/parser::parse() error Error {} which is completely useless for debugging.
My directory structure is: /index.html(main DOM) /app/DrawingToolWidget/templates/DrawingToolsWidget.html(template) /app/DrawingToolWidget/DrawingToolWidget.js(declaration)
I've also tried to attach "app/DrawingToolWidget/DrawingToolWidget" just in case it wanted the path to the widget declaration. Nope.
What am I doing wrong? Why is it so hard just to attach a fragment of markup to the DOM?

Your template is invalid because you can only have 1 root element. I'm not sure if that's the main problem, but this might solve certain things. A quote from the documentation:
Note that when you define a template, it can only have one root
node definition ( just like with XML documents). Multiple nodes at the
top level is not allowed.
The article: http://dojotoolkit.org/documentation/tutorials/1.9/templated/
A working template would be:
<div>
<div class="tool">
<a href="" id="freehand">
<i class="flaticon-writing9"></i>
</a>
</div>
<div class="tool">
<a href="" id="polygon">
<i class="flaticon-constellation"></i>
</a>
</div>
</div>
The working example can be found here.
About your debugging problem, do you use Google Chrome? When encountering an error, you should try to close and open the developer tools/console, if you do that, there should be a small arrow next to the Error, for example:
As you can see here, there are 2 arrows, the left one gives you the stacktrace, while the right one (almost at the end of the error line), will give you detailed information about the error (if you click/expand it).

Related

v-on:click not working - dynamically added html element

I am trying to get all images for a category using Vue
<div class="col-md-12 col-sm-2 p-2">
<a v-on:click="onCategoryManageImageClick($event)" data-target="#location-
category-images">
</span>
</a>
</div>
So the event onCategoryManageImageClick ($event) does not work, if I am adding a html block and then click on get image button.
this is index.js
methods:{
onImagesTabClick(){
this.$root.$emit('activated-tab:location-images');
},
onCategoriesTabClick(){
window.j1App.eventBus.$emit("j1-location-image-list:shown");
},
onCategoryManageImageClick: function(event) {console.log('working event..');
event.preventDefault();
window.j1App.eventBus.$emit("j1-location-category-image-
list:shown",event.currentTarget.id);
}
}
So basically it need to to work like we do in jQuery
$(document).on('click',function{
})
So it works either page load or if adding any new html element on DOM. same I want in Vue.
You cannot alter the Vue template outside of Vue. That won't work. Vue compiles the template once when starting up and adds the event listeners to the rendered elements. If you add elements afterwards, Vue will not know about them.
The correct way of doing this would be to add those new elements in Vue.
<div
class="col-md-12 col-sm-2 p-2"
v-for="item in items"
:key="item.id"
>
<a
v-on:click="onCategoryManageImageClick($event, item)"
data-target="#location-category-images"
>
</a>
</div>
See https://v2.vuejs.org/v2/guide/list.html for documentation. In this case you need the items array variable in data and add more array elements to it, if you need more links.
Try raplacing your a tag with p
<div class="col-md-12 col-sm-2 p-2">
<p v-on:click="onCategoryManageImageClick" data-target="#location-
category-images">
</p>
</div>

Issue with JSViews and Materialize Dropdown Button

I have an issue with using a linked template with JSViews and Materialize Dropdown Button.
The first time, the view is rendered, it works fine, however as soon as I observably update the status of the button, the ul tag which is linked to the button is deleted from the DOM.
I have created a test case on JSFiddle: here
<script id="test_template" type="text/x-jsrender">
{^{for tasks}}
<div class="dropdown change-state" style="display: block">
{^{if Status == 0}}
<a href="#" class="completed-state-box dropdown-button btn btn-flat red" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>NOT COMPLETE</a>
{{/if}}
{^{if Status == 1}}
<a href="#" class="completed-state-box dropdown-button btn btn-flat green" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>COMPLETE</a>
{{/if}}
<ul id="review_status_dropdown-{{:Id}}" class="dropdown-content">
<li><a class="state-change-button" href="#" data-value="0">NOT COMPLETE</a></li>
<li><a class="state-change-button" href="#" data-value="1">COMPLETE</a></li>
</ul>
</div>
{{/for}}
If you click on a button after running, it opens the drop down just fine. If you select an option that is not set (ie set the COMPLETED button to NOT COMPLETE), it updates the UI, but as the ul is removed from the DOM, clicking on it again does not reveal the drop down. Only the button that is updated is affected.
Can anyone see where I am going wrong?
It looks like Materialize clones the ul and inserts a copy after each activator. Initially there is only one activator (since the other {^{if}} expression is false so its content is empty) - and the cloned ul is placed next to it inside the {^{if}} block.
When you click to toggle the status, the contents of that {^{if}} get removed, and the other {^{if}} contents get rendered. But the second activator (<a> tag) has not been 'activated' and so does not have an associated <ul>.
If you are going to use Materialize alongside JsViews, then you have to deal with Materialize potentially doing DOM manipulation, which can break the JsViews data linking. Having two frameworks which both manipulate the same DOM can lead to collisions or conflicts.
In this case you can resolve the problem by using the visible binding, rather than {^{if}}:
<div class="dropdown change-state" style="display: block">
<a href="#" data-link="visible{:Status==0}" class="completed-state-box dropdown-button btn btn-flat red" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>NOT COMPLETE</a>
<a href="#" data-link="visible{:Status==1}" class="completed-state-box dropdown-button btn btn-flat green" data-constrainwidth="false" data-activates='review_status_dropdown-{{:Id}}'>COMPLETE</a>
<ul id="review_status_dropdown-{{:Id}}" class="dropdown-content">
<li><a class="state-change-button" href="#" data-value="0">NOT COMPLETE</a></li>
<li><a class="state-change-button" href="#" data-value="1">COMPLETE</a></li>
</ul>
</div>
Update:
In response to grayson's Materialize-related issues of 'inline-block' vs 'block',
here are a couple of alternatives:
You can data-link directly to the CSS display attribute, and specify the values you want to toggle:
<a data-link="css-display{:Status==1?'inline-block':'none'}" ...>
Or you can create a custom tag that does what you want:
$.views.tags("show", {
render: function(val) {
return val ? "inline-block" : "none"
},
attr: "css-display"
});
and then write:
<a data-link="{show Status==0}" ...>
You can even do:
$.views.tags("show", {
render: function(val, type) {
return val ? type||"inline-block" : "none"
},
attr: "css-display"
});
and write
<a data-link="{show Status==0}" ...>
or
<foo data-link="{show Status==0 'block'}" ...>
Boris' Answer is the correct answer to this question as you would obviously expect, however, I wanted to add a little something which may help anyone in the situation.
JSViews is very clever and using the visible binding, it appears to try and identify the type of element, so in the case of an anchor tag it uses display:inline, whereas for a div it uses display:block
In the case of materialize (and bootstrap), the btn class sets the display to inline-block, so using visible binding doesn't work as intended in all cases.
In my case, because this truly was a button rather than a link, I changed the anchor tag to a button tag and as this is a block element it worked just fine.
It is not an ideal solution, as in my case, I might have 30 buttons, with 5 options which means flooding the DOM with lots of extra hidden tags, but until I can come up with a better solution, using the {^{if..., it works.

change active tab on document ready

I would like to change the active pill/tab on document load. I know you can set the active pill like I have below but for other reasons I want to change it after document load. I have tried various bits of JS but nothing seems to work. Here's the HTML and JS (I have also tried replacing data-toggle="pill" with data-toggle="tab" below and still doesn't work).
<div>
<ul class="nav nav-pills pillstyle">
<li class="active tabstyle"><a data-toggle="pill" href="#apple">Apple</a></li>
<li class="tabstyle"><a data-toggle="pill" href="#banana">Banana</a></li>
<li class="tabstyle"><a data-toggle="pill" href="#pear">Pear</a></li>
<li class="tabstyle"><a data-toggle="pill" href="#orange" >Orange</a></li>
</ul>
</div> <!-- nav pills close -->
<div class="tab-content">
<div id="apple" class="tab-pane fade in active"> `
.... content of tabs.
JS
$(document).ready(function(){
$('#banana').tab('show');
});
or
$(document).ready(function(){
$('#banana').pill('show');
});
You just need to change your jQuery selector to address the a element instead of the tab-pane div.
$(document).ready(function(){
$('a[href="#banana"]').tab('show');
});
If you need, you can find more detailed description about bootstrap tabs in the official documentation.
#Stu Here you go.
HTML:
Assign an ID myTab to UL element.
<ul class="nav nav-pills pillstyle" id="myTab">
JS:
$(function () {
$('#myTab a[href="#banana"]').tab('show');
});
Also refer to Bootstrap documentation on selecting different elements on load here. It will give you better understanding.
http://getbootstrap.com/2.3.2/javascript.html#tabs
Working demo: https://jsfiddle.net/tf9k9j27/
Note: Just to answer your trial and error.
You can activate a tab or pill navigation without writing any JavaScript by simply specifying data-toggle="tab" or data-toggle="pill" on an element. Adding the nav and nav-tabs classes to the tab ul will apply the Bootstrap tab styling. (From bootstrap docs. read more to get better clarity)

Can't require('durandal/app') in shell.js

This is my shell.html defining a navbar, pretty much lifted straight from the tute.
<div>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<ul class="nav" data-bind="foreach: router.navigationModel">
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }"><span><i data-bind="attr: { class: glyph }"></i> <span data-bind="text: title"></span></span></a>
</li>
</ul>
<span class="nav">
Welcome <span data-bind="text: app.user.name()"></span>
</span>
<div class="loader pull-right" data-bind="css: { active: router.isNavigating }">
<i class="icon-spinner icon-2x icon-spin"></i>
</div>
</div>
</div>
<div class="container-fluid page-host" data-bind="router: { transition:'entrance' }"></div>
</div>
This is the problem part:
<span data-bind="text: app.user.name()"></span>
user is a property I add to app right after it's created. It is updated by the view models for log in and log out.
To get that binding to work I need to bring the object into scope in the view model. Normally I would do that like this: var app = require('durandal/app');. In most view models this is no problem. But view management gets seriously messed up when I do it in shell.js.
All I'm trying to do is fish out some app state to indicate whether the user is currently logged in.
There are two possible solutions
Get app into scope somehow
Manage application state in some other way that is in scope in shell.js
I'm sure this is trivial to old hands; I await your wisdom. Searching google didn't work well because "app" is way too broad a search term.
I dodged the problem by tacking user onto document instead of app, but that's hideous. I'm all ears for the right way to do this.
Interestingly, HTML5 browsers define sessionStorage for exactly the purpose to which I polluted document. If you do this in your boot code
if (typeof(document.sessionStorage) == undefined) document.sessionStorage = {};
then you can assume it exists in the rest of your app.
We can't count on browser support, but it's not hard to check and if necessary create it. If you look up sessionStorage you'll see a warning that the contents will be lost at the next page load, but in a SPA that matters not at all.
One way to accomplish this (most probably the right one), would be using a shared AMD module as discussed in Session Data with Durandal.
What we did in our Durandal app is create a separate static module called config, which contains, among other properties, an observable property called userName.
At the top of your module (the shell.js file, in this case), put the following:
define([
'durandal/system',
'durandal/activator',
'plugins/router',
'durandal/app',
...
'config'
],
function(system, activator, router, app,..., config) {
}
);
The config module should be static, which means that it should return an object literal, not a constructor function.
Your config module might look something like this (bare bones):
define('config', ['knockout'],
function(ko) {
var userName = ko.observable('');
return {
userName: userName
};
}
);

Need the HTML content of the current JQUERY-UI Tab

<div id="tabs">
<ul>
<li>Tab One</li>
<li>Tab Two</li>
<li>Tab Three</li>
</ul>
</div>
I have a tab widget, displaying content from different urls.
I need an expression, to get the contents (HTML string) of the currently displayed content, in the tab panel. Something like
alert($('tabs').tabs('option', 'selectedpanel').innerHTML);
The only reference I have is the JQuery documentation http://api.jqueryui.com/tabs/ , but I could not get an suitable answer from the API documentation there.
Just use the load event of the tabs as below :
<script>
$(function() {
$( "#tabs" ).tabs({
load: function( event, ui ) {
alert(ui.panel.html());
}
});
});
</script>