Determining the type of a standard jQueryUI widget - jquery-ui

I am having trouble determining the type of a given jQueryUI widget instance.
The jQueryUI documentation for the Widget Factory suggests two techniques. From the "Instance" section:
The widget's instance can be retrieved from a given element using the
instance() method. [...]
If the instance() method is called on an element that is not
associated with the widget, undefined is returned.
the :data selector can also determine whether an element has a given
widget bound to it.
Based on their examples, let's say I initialize a datepicker and later code checks if it is a datepicker:
<p>Date: <input type="text" id="datepicker"> </p>
$(function() {
$("#datepicker").datepicker();
// ...
var i = $("#datepicker").progressbar("instance"); // i is undefined as expected
console.log(i);
var b = $("#datepicker").is(":data('ui-datepicker')"); // b = false, not sure why
console.log(b);
var i2 = $("#datepicker").datepicker("instance"); // this throws an exception
console.log(i2);
});
Based on the documentation I expected the .is call to return true, and the last line to return the instance (not throw an exception.)
JSFiddle is here. (You will need to open the browser's console to see the logged output.)

It turns out the techniques I listed above do work for many jQueryUI widgets, e.g. button, progressbar.
But datepicker is kind of weird. Looking at the DOM after a datepicker is initialized, I see the datepicker is inserted as a new element after the named element.
To get the datepicker widget instance I'd need to navigate the DOM starting from the named element. To check if the input field has a datepicker on it we can simply check the element for the class hasDatepicker:
var isDatePicker = $("#datepicker").is(".hasDatepicker");
This works with jQueryUI 1.11.2, and based on other SO questions it's been working since 2009. So I guess it's a reliable technique, but I'm not sure if its documented anywhere, or guaranteed for future versions.

Related

How to query for an element inside a dom-repeat

I have been scouring the web for a clear answer on how to query for an element generated by a dom-repeat element from Dart code.
sample.html
<dom-module id="so-sample>
<style>...</style>
<template>
<template is="dom-repeat" items="[[cars]] as="car>
...
<paper-button on-click="buttonClicked">Button</paper-button>
<paper-dialog id="dialog">
<h2>Title</h2>
</paper-dialog>
</template>
</template>
sample.dart
I'll omit the boilerplate code here, such as imports or the query to my database to fill the cars property ; everything works fine.
...
#reflectable
void buttonClicked(e, [_])
{
PaperDialog infos = this.shadowRoot.querySelector("#dialog");
infos.open();
}
This generates the following error :
Uncaught TypeError: Cannot read property 'querySelector' of undefined
I have tried several 'solutions', which are not, since nothing works.
The only thing I saw on quite a lot of threads is to use Timer.run() and write my code in the callback, but that seems like a hack. Why would I need a timer ?
I understand my problem may be that the content of the dom-repeat is generated lazily, and I query the items 'before' they are added to the local DOM.
Another advice I didn't follow is to use Mutation Observers. I read in the polymer API documentation that the observeNodes method should be used instead, as it internally uses MO to handle indexing the elements, but it again seems a bit complicated just to open a dialog.
My final objective is to bind the button of each generated model to a dedicated paper-dialog to display additional information on the item.
Has anyone ever done that ? (I should hope so :p)
Thanks for your time !
Update 1:
After reading Gunter's advices, although none of them actually worked by themselves, the fact that the IDs aren't mangled inside a dom-repeat made me think and query paper-dialog instead of the id itself, and now my dialog pops up !
sample.dart:
PaperDialog infos = Polymer.dom(root).querySelector("paper-dialog");
infos.open();
I now hope that each button will call the associated dialog, since I'll bind data inside the dialog relative to the item I clicked ~
Update 2:
So, nope, the data binding didn't work as expected: All buttons were bound to the item at index 0, just as I feared. I tried several ways to query the correct paper-dialog but nothing worked. The only 'workaround' I found is to query all the paper-dialog into a list and then get the 'index-th' element from that list.
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
List<PaperDialog> dialogs = Polymer.dom(this.root).querySelectorAll("paper-dialog");
dialogs[model.index].open();
}
This code definitely works, but it feels kind of a waste of resources to get all the elements when you really only need one and you already know which one.
So yeah, my initial problem is solved, but I still wonder why I couldn't query the dialogs from their id:
...
<paper-dialog id="dialog-[[index]]">
...
</paper-dialog>
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
PaperDialog dialog = Polymer.dom(this.root).querySelector("dialog-${model.index}");
dialog.open();
}
With this code, dialog is always null, although I can find those dialogs, correctly id-ied, in the DOM tree.
You need to use Polymers DOM API with shady DOM (default). If you enable shadow DOM your code would probably work as well.
PaperDialog infos = new Polymer.dom(this).querySelector("#dialog")

Dart web-ui not updated when data received from network

I have the following fragment in a web component:
<div id="mycodes">
<template iterate='code in codeList'>
{{code}}
</template>
</div>
And in a Dart file, codeList is populated when the user clicks on a button:
void onMyButtonClick(Event event) {
HttpRequest.getString('http://getData').then((response) {
mylist = json.parse(response);
for(var code in mylist){
codeList.add(code['c']);
}
}
The problem is that I don't see data on first click. I need to click the button twice to see data.
But if I fill codeList manually (not from network data) as shown below, then I see the data on first click:
void onMyButtonClick(Event event) {
codeList.add("data 1");
codeList.add("data 2");
}
}
I need the template to iterate after the network data is available. It appears that event loop has already done its job of painting a page before the network data becomes available through future object.
Is there a way to refresh the page after model is updated in dart?
The reason your codeList currently populates if you add it with the on-click event is because the current web_ui has 'watchers' which automatically are called when an event happens. You then populate the list synchronously. However one of the downfalls of watchers is exactly your use case, when the data is updated asynchronously then the watchers don't reflect changes in time.
As a result the watchers are being phased out and replaced with observables. Observables allow us to flag a variable to be watched for reassignment and when that happens it will cause the view to change. For example:
#observable int x = 0;
// ...
x = 1;
When the x = 1 is called later in the code it automatically triggers the views to update. This leaves us with one problem however. When you are adding to a list, you are not reassigning the value itself. As such, observables also offer a function to convert a list to an observable list (this also works for maps).
For instance if you changed your declaration of codeList to something like the following, then when you add to the list later it will update accordingly.
var codeList = toObservable([]); // Assuming it starts with an empty list
// or
var codeList = toObservable(_startCodeList); // if you already have a list
Also see the Dart Tutorial: Target 7 for more information on using #observable and toObservable.
For more in-depth information, check out the article on Observables and Data Binding
You need to mark the fields you want WebUi to monitor with the #observable annotation. Otherwise you only get the initial value not any subsequent updates.
You can do this either directly on the object declaration or you can make the entire class as observable and all its fields will then be observed.
For an example see http://www.dartlang.org/docs/tutorials/custom-elements/#using-two-way-data-binding

Knockout.js - in a binding access observable, not just it's value

I have the following custom binding
ko.bindingHandlers.dialogFor = {
init: function(el, valueAccessor) {
$(el).dialog();
var val = valueAccessor();
if(ko.isObservable(val))
$(el).on('dialogclose', function(){
val(null)
});
}
};
This allows me to tag an html fragment with a dialogFor binding and open dialogs simply by setting observables.
Unfortunately, the value that gets passed in is always unwrapped so the if check never passes and resetting the observable on dialog close doesn't work.
How do I get the actual observable that was passed, not just the unwrapped value?
I can't see your HTML so its a guess, i think you are using observable in your custom binding's value like :
<div data-bind="dialogFor: val()"></div>
if yes than you already unwrapped the value and only value not the observable is passing to your custom binding, so that's why your if condition is failing.You should use it like :
<div data-bind="dialogFor: val"></div>
I have created a working example in which jquery ui dialog state is controlling by observable.

Binding stops updating

I've been trying to create a custom binding for updating flot charts and it seems like it works when it first loads, but as I navigate around it quits.
Here's the scenario, I have a list view on one page (this is in jQuery Mobile) with little thumb nails of graphs, next to the graph is a slider that is bound to a property of the same view model that causes the graph points to be recalculated. When you click on one of the list items, it moves to another page that shows a much larger version of the graph and lets you change the value by typing in a textbox (later, you'll be able to click directly on the graph). The binding looks something like this:
ko.bindingHandlers["plot"] = {
init: function (element, valueAccessor, allBindingsAccessor) {
var qe = $(element);
var page = qe.closest("div[data-role='page']");
page.bind("pageshow", function () {
ko.bindingHandlers["plot"].update(element, valueAccessor);
});
},
update: function (element, valueAccessor, allBindingsAccessor) {
var qe = $(element);
var page = qe.closest("div[data-role='page']");
var curr = $.mobile.activePage;
var val = ko.utils.unwrapObservable(valueAccessor());
var data = val.plotData();
if(data && page.prop("id") == curr.prop("id")) {
var marker = val.markerData();
var opt = val.chartOptions();
opt.yaxis.show = opt.xaxis.show = !qe.hasClass("graphThumb");
marker.points.radius = opt.yaxis.show ? 5 : 3.5;
$.plot(qe, [
data,
marker
], opt);
}
}
};
The init handler sets it up to draw the graph on a page show because flot doesn't work right when drawing to a non-visible div. The update will check if the currently displayed page is the same as the one with the binding and redraw the graph as required.
For the graphs in the list view, they are immediately draw by the update method and work correctly. However, for the initially hidden pages, the function to draw the graph fires, the graph draws, but the updates will no longer work. Then, worse, when you go back to the initial page, the function bound to the pageshow event fires, redraws the graphs, but now they've also quit updating.
The view model looks something like this:
var viewModel = (function () {
this.current = ko.observable(0);
this.plotData = ko.computed(function () {
var points = [];
// a bunch of calculations that depend on the value of current of this and other viewModels in a collection
return points;
}
}
I can stick a break point in the computed plotData and see that it is getting update correctly. It just that those updates aren't trigger the binding handler.
The HTML binding looks something like this:
<!-- the first, visible page -->
<div data-role="page" id="index">
<ul data-role="listview" data-bind="foreach: factors">
<li data-bind="attr: {id: listId}">
<a data-bind="attr: {href: idLink}">
<div class="graphThumb" data-bind="plot: $data"></div>
</a>
</li>
</ul>
</div>
<!-- hidden details pages -->
<!-- ko foreach: factors -->
<div data-role="page" data-bind="attr: { id: id }">
<div class="graphPlaceHolder" data-bind="plot: $data"></div>
</div>
<!-- /ko -->
Update: I changed my binding slightly because I realized that I can just call the update on the pageshow event handler, which simplifies things, but doesn't fix the problem. It seems that doing that won't make knockout update it's dependencies for the binding.
Update: another update, assigning val.plotData() to a variable didn't work, neither did including it in my if statement. However, I have another computed observable that depends on the the current value and another property of the parent view model that I could retrieve and add to my if statement that works! However, my solution is probably to specific to be generally useful. The short story is that knockout will reassess the dependencies of a binding with each update, so you need to make sure that it is evaluating something important regardless of any conditional logic or it will stop updating.
So I can wrap up and mark this question as answered, I will briefly summarize my experience.
Custom binding are implemented the same was as computed properties in Knockout (according to the KO docs), and one thing that computed properties do is reassess which properties they are dependent on every time they are executed. What this means, is if you have a conditional in computed property (or a custom binding), only the properties accessed in the branch of the condition that actually gets executed will be monitored for changes by knockout. So, for example, if you have a property like this:
var myComputedProperty = ko.computed(function() {
if(this.myBool()) {
$("#someElement").text(this.foo());
}
else {
$("#someElement").text(this.bar());
}
});
KO will keep track of the value of myBool and recalculate the property if it changes, but, if myBool is true, it will also track foo, if myBool is false, it will also track bar, but it won't track both - because it doesn't need to. Most of the time this works just fine.
In my case it failed because I had a conditional that wasn't part of the view model (and therefore wasn't observable) and I needed it to keep track of the view model properties regardless of whether or not the condition evaluated to true or false. So I had something that looked like this:
if(page.prop("id") == curr.prop("id")) {
$("#someElement").text(this.foo());
}
Here the comparison is between the id of the page that the binding lives on and the $.mobile.activePage provided by jQuery Mobile (and, obviously not observable). If those id match, then knockout will update the binding when foo changes. However, if they don't, then knockout will lose the dependency on foo and even if the id do match at some later time, it will have lost the dependency and won't reevaluate when foo changes.
The way around this is to ensure that any properties that need to be tracked are evaluated regardless of the condition. So, something like this should solve the general case:
if(this.foo() && page.prop("id") == curr.prop("id")) {
$("#someElement").text(this.foo());
}
As to why I needed the condition at all is because flot gets very confused when it tries to draw a graph to a non-visible div, so I need to skip drawing the graph when it wasn't the current page.

how to refresh jquery mobile listviews

I am attempting to refresh a listview after I change the list items in it but it keeps failing with the message:
"Uncaught cannot call methods on listview prior to initialization;
attempted to call method 'refresh'".
The list is an unordered list in a div with a data-role="dialog" attribute. The list has data-role="listview". After updating, if I call $('#theULInQuestion').listview('refresh'), I get the error message. What gives?
Thanks,
Rob
There are a lot of page-events in the jQuery Mobile framework which makes it hard to determine which is the proper one to bind to. I bind to the pageshow event because by the time that event fires, the DOM is prepared (jQuery Mobile has initialized everything).
However whenever I am having issues with timing the refresh of a jQuery Mobile widget I check to see if it has been initialized and run the necessary code to either initialize or refresh the widget.
$(document).delegate('#my-dialog-id', '<page-event>', function () {
var $the_ul = $('#theULInQuestion');
//add your rows to the listview here
$the_ul.append('<li>a row</li><li>another row</li>');
if ($the_ul.hasClass('ui-listview')) {
$the_ul.listview('refresh');
} else {
$the_ul.trigger('create');
}
});
Each of the jQuery Mobile widgets have specific classes that are added to that type of widget. So you can check (after appending your rows) if the widget has the jQuery Mobile classes; if it does then refresh it, if it doesn't then initialize it.
Here is a jsfiddle: http://jsfiddle.net/jasper/urTeW/1/
Note that you can place the conditional statement to refresh/initialize anywhere in your code, it doesn't have to happen when a page-event fires.

Resources