How do you callback into Dart from jquery with parameters? - dart

I'm writing a Dart class to wrap the jqueryUI slider. The jquery code looks like:
$("#diameter_slider").slider({'range': true, max: 50, min: 0, values: [0, 50],
slide: function( event, ui ) {
//code executed when the slider is moved goes here
});
My Dart class looks like:
class Slider {
Map options;
String css_id;
String units;
var slider;
Slider( {this.options, this.css_id, this.units} ) {
js.scoped((){
slider = js.context.$(css_id);
final updater = new js.Callback.many((d, i, context) { query("#diameter").text = 'here';});
final opts = js.map({ 'range': true,
'values': [options['min'], options['max']],
'slide': updater});
slider.slider(opts);
js.retain(slider);
});
}
void UpdateText( var event, var ui ){
query("#diameter").text = 'here';
}
}
When executed the slider displays correctly (using Dartium). But When the slider is moved Dartium crashes. What is the correct way to get parameters passed to Dart from javascript?

Looking at the slide doc, the callback function seems to handle 2 parameters. So, your updater should be defined like this :
final updater = new js.Callback.many((event, ui) { query("#diameter").text = 'here';});

Related

What does void Function() do in Dart?

I´m quite new to dart and wondering what this "wrapped" function exactly does?
It´s called like a normal function with "connectUnits(userRepo)":
void Function(
Store<AppState> store,
dynamic action,
NextDispatcher next,
) connectUnits(
UnitsRepository unitsRepository,
) {
return (store, action, next) {
unitsRepository.units().listen((units) {
store.dispatch(LoadUnitsAction(units));
next(action);
});
};
}
Thanks & best,
Michael
Functions are first class citizens in Dart. Your example defines a function named connectUnits that returns a function with a signature void Function(Store<AppState> store, dynamic action, NextDispatcher next).
To better understand, your code is the same as:
// define a kind of function
typedef MyFunction = void Function(Store<AppState> store, dynamic action, NextDispatcher next);
MyFunction connectUnits(UnitsRepository unitsRepository) {
return (store, action, next) {
unitsRepository.units().listen((units) {
store.dispatch(LoadUnitsAction(units));
next(action);
});
};
}

Vaadin: open calendar on field focus for datefield

Vaadin widgets are simple and awesome! But they are also poorly configurable.
I need my DateField widget to open calendar on focus event. I didn't find that functionality in official Vaadin documentation. I found some 3rd party widget here, but it's compiled for Vaadin 7.7 and I use latest Vaadin (8.0.6). Also it has Joda-time 2.1 dependency which is highly undesirable in my project. So, is there any simple way to tune stock vaadin DateField widget to open it's calendar on field focus, or do I need to write my own component for that? Any help is appreciated.
As I was saying in my comment, as far as I know, currently the framework does not offer an implicit way to programmatically open the calendar popup. The same thing goes for some other components such as the grid editor, or the combo item list.
One quick workaround I can think of, is to add a javascript extension that registers focus listeners for all date fields, and clicks the button when a date field is focused. Please find below a sample.
P.S. If you only need to apply this to only some date fields, you can add IDs and pass them to the JS, where you'll do something like document.getElementById('myDateFieldId') instead of document.getElementsByClassName("v-datefield").
1) Layout with components
public class MyDateFieldComponent extends HorizontalLayout {
public MyDateFieldComponent() {
// basic setup
DateField fromDateField = new DateField("From", LocalDate.of(2011, Month.FEBRUARY, 6));
DateField toDateField = new DateField("To", LocalDate.of(2018, Month.FEBRUARY, 6));
setSpacing(true);
addComponents(fromDateField, toDateField);
// add the extension
addExtension(new CalendarFocusPopupOpenerExtension());
}
}
2) Extension - java/server side
import com.vaadin.annotations.JavaScript;
import com.vaadin.server.AbstractJavaScriptExtension;
#JavaScript("calendar-focus-popup-opener-extension.js")
public class CalendarFocusPopupOpenerExtension extends AbstractJavaScriptExtension {
public CalendarFocusPopupOpenerExtension() {
// call the bind function defined in the associated JS
callFunction("bind");
}
}
3) Extension - js/client side
window.com_example_calendar_CalendarFocusPopupOpenerExtension = function () {
this.bind = function () {
if (document.readyState === "complete") {
// if executed when document already loaded, just bind
console.log("Doc already loaded, binding");
bindToAllDateFields();
} else {
// otherwise, bind when finished loading
console.log("Doc nod loaded, binding later");
window.onload = function () {
console.log("Doc finally loaded, binding");
bindToAllDateFields();
}
}
};
function bindToAllDateFields() {
// get all the date fields to assign focus handlers to
var dateFields = document.getElementsByClassName("v-datefield");
for (var i = 0; i < dateFields.length; i++) {
addFocusListeners(dateFields[i]);
}
}
function addFocusListeners(dateField) {
// when focusing the date field, click the button
dateField.onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
// or when focusing the date field input, click the button
dateField.getElementsByTagName("input")[0].onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
}
};
4) Result
LATER UPDATE
A second approach could be to assign some IDs to your fields, and then check periodically to see when all are visible, and as soon as they are, bind the focus listeners.
1) Layout with components
public class MyDateFieldComponent extends HorizontalLayout {
public MyDateFieldComponent() {
// basic setup
DateField fromDateField = new DateField("From", LocalDate.of(2011, Month.FEBRUARY, 6));
fromDateField.setId("fromDateField"); // use id to bind
fromDateField.setVisible(false); // initially hide it
DateField toDateField = new DateField("To", LocalDate.of(2018, Month.FEBRUARY, 6));
toDateField.setId("toDateField"); // use id to bind
toDateField.setVisible(false); // initially hide it
// simulate a delay until the fields are available
Button showFieldsButton = new Button("Show fields", e -> {
fromDateField.setVisible(true);
toDateField.setVisible(true);
});
setSpacing(true);
addComponents(showFieldsButton, fromDateField, toDateField);
// add the extension
addExtension(new CalendarFocusPopupOpenerExtension(fromDateField.getId(), toDateField.getId()));
}
}
2) Extension - java/server side
#JavaScript("calendar-focus-popup-opener-extension.js")
public class CalendarFocusPopupOpenerExtension extends AbstractJavaScriptExtension {
public CalendarFocusPopupOpenerExtension(String... idsToBindTo) {
// send the arguments as an array of strings
JsonArray arguments = Json.createArray();
for (int i = 0; i < idsToBindTo.length; i++) {
arguments.set(i, idsToBindTo[i]);
}
// call the bind defined in the associated JS
callFunction("bind", arguments);
}
}
3) Extension - js/client side
window.com_example_calendar_CalendarFocusPopupOpenerExtension = function () {
var timer;
this.bind = function (idsToBindTo) {
// check every second to see if the fields are available. interval can be tweaked as required
timer = setInterval(function () {
bindWhenFieldsAreAvailable(idsToBindTo);
}, 1000);
};
function bindWhenFieldsAreAvailable(idsToBindTo) {
console.log("Looking for the following date field ids: [" + idsToBindTo + "]");
var dateFields = [];
for (var i = 0; i < idsToBindTo.length; i++) {
var dateFieldId = idsToBindTo[i];
var dateField = document.getElementById(dateFieldId);
if (!dateField) {
// field not present, wait
console.log("Date field with id [" + dateFieldId + "] not found, sleeping");
return;
} else {
// field present, add it to the list
console.log("Date field with id [" + dateFieldId + "] found, adding to binding list");
dateFields.push(dateField);
}
}
// all fields present and accounted for, bind the listeners!
clearInterval(timer);
console.log("All fields available, binding focus listeners");
bindTo(dateFields);
}
function bindTo(dateFields) {
// assign focus handlers to all date fields
for (var i = 0; i < dateFields.length; i++) {
addFocusListeners(dateFields[i]);
}
}
function addFocusListeners(dateField) {
// when focusing the date field, click the button
dateField.onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
// or when focusing the date field input, click the button
dateField.getElementsByTagName("input")[0].onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
}
};
4) Result

Distinguish between onClick and onDoubleClick on same element to perform different actions in Dart

I want to do the ill-advised and place both an onClick and onDoubleClick on the same element with each type of event resulting in a different action. Specifically on an image, click to advance to the next image, double-click to toggle fullscreen.
Naturally I get two clicks followed by a double-click (though I understand that some browsers only fire one click before the double-click).
I had thought to make it easy on myself and place each event into a buffer (List) - or rather to add the event.type string to a list, then, after a suitable elapse of time, say 250 or 300 milliseconds examine the last item in the buffer and if doubleclick then go fullscreen else advance the length of the list.
I have found that the list only ever has one item, and I have not worked out how to get the timer to work..
Amongst my attempts was this one:
void catchClickEvents(Event e) {
var eventTypes = new List<String>();
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
// just to see what is going on...
print(eTypes);
}
this results in this output printed to the console:
[click]
[click]
[dblclick]
rather than
[click, click, dblclick]
If I slow it down there is a clear delay before those three event types are printed together
So...
The bigger question is
'What is the darty way to distiguish between single and double-click and perform a different action for each?'
The other questions are:
How do I fill a buffer with successive events (and later clear it down) - or even how do I use Dart's Stream of events as a buffer...
What is the real way timeout before examining the contents of the buffer?
(and I guess the final question is 'should I abandon the effort and settle for a conventional set of buttons with glyph-icons?'!)
I'm not sure if IE still has the event sequence explained here (no 2nd click event)
https://stackoverflow.com/a/5511527/217408
If yes you can use a slightly deviated variant of Roberts solution:
library app_element;
import 'dart:html' as dom;
import 'dart:async' as async;
Duration dblClickDelay = new Duration(milliseconds: 500);
async.Timer clickTimer;
void clickHandler(dom.MouseEvent e, [bool forReal = false]) {
if(clickTimer == null) {
clickTimer = new async.Timer(dblClickDelay, () {
clickHandler(e, true);
clickTimer = null;
});
} else if(forReal){
print('click');
}
}
void dblClickHandler(dom.MouseEvent e) {
if(clickTimer != null) {
clickTimer.cancel();
clickTimer = null;
}
print('doubleClick');
}
void main() {
dom.querySelector('button')
..onClick.listen(clickHandler)
..onDoubleClick.listen(dblClickHandler);
}
or just use Roberts solution with the mouseUp event instead of the click event.
The problem is that your variable is not global.
var eventTypes = new List<String>();
void catchClickEvents(Event e) {
eventTypes.add(e.type);
Duration duration = const Duration(milliseconds: 300);
var timeout = new Timer(duration, () => processEvents(eventTypes));
}
void processEvents(List eTypes) {
print(eTypes);
}
There also is e.detail that should return the number of the click. You can use that, if you don't need the Internet Explorer. The problem with your list is that it grows and never gets cleared.
Let's take into account what we know: You get click events and at somepoint you have doubleclicks.
You could use a click counter here. (Or use e.detail) to skip the second click event. So you only have click and dblclick.
Now when you get a click event, you create a new timer like you did before and run the click action. If you get the dblclick event you simply run you action. This could like this:
DivElement div = querySelector('#div');
Timer timeout = null;
div.onClick.listen((MouseEvent e) {
if(e.detail >= 2) {
e.preventDefault();
} else {
if(timeout != null) {
timeout.cancel();
}
timeout = new Timer(new Duration(milliseconds: 150), () => print('click'));
}
});
div.onDoubleClick.listen((MouseEvent e) {
if(timeout != null) {
timeout.cancel();
timeout = null;
}
print('dblclick');
});
This is the example code that works for me. If you can't rely on e.detail just us a counter and reset it after some ms after a click event.
I hope this helps you.
Regards, Robert
Your page should react on user inputs as fast as possible. If you wait to confirm double click - your onClick will become much less responsive. You can hide the problem by changing visual state of the element(for example, playing animation) after first click but it can confuse user. And it gets even worse with handheld. Also if element has to react only on onClick event, you can "cheat" and listen to onmousedown instead - it will make your UI much more responsive.
On top of all this, double click, from client to client, may have noticeably different trigger time interval and it isn't intuitive, for user, that you can double click something. You will have to bloat your interface with unnecessary hints.
edit: Added my solution. It should be fairly extensible and future proof.
import 'dart:html';
import 'dart:async';
import 'dart:math';
//enum. Kinda... https://code.google.com/p/dart/issues/detail?id=88
class UIAction {
static const NEXT = const UIAction._(0);
static const FULLSCREEN = const UIAction._(1);
static const ERROR = const UIAction._(2);
final int value;
const UIAction._(this.value);
}
/**
*[UIEventToUIAction] transforms UIEvents into corresponding UIActions.
*/
class UIEventToUIAction implements StreamTransformer<UIEvent, UIAction> {
/**
* I use "guesstimate" value for [doubleClickInterval] but you can calculate
* comfortable value for the user from his/her previous activity.
*/
final Duration doubleClickInterval = const Duration(milliseconds: 400);
final StreamController<UIAction> st = new StreamController();
Stream<UIAction> bind(Stream<UIEvent> originalStream) {
int t1 = 0,
t2 = 0;
bool isOdd = true;
Duration deltaTime;
originalStream.timeout(doubleClickInterval, onTimeout: (_) {
if ((deltaTime != null) && (deltaTime >= doubleClickInterval)) {
st.add(UIAction.NEXT);
}
}).forEach((UIEvent uiEvent) {
isOdd ? t1 = uiEvent.timeStamp : t2 = uiEvent.timeStamp;
deltaTime = new Duration(milliseconds: (t1 - t2).abs());
if (deltaTime < doubleClickInterval) st.add(UIAction.FULLSCREEN);
isOdd = !isOdd;
});
return st.stream;
}
}
void main() {
//Eagerly perform time consuming tasks to decrease the input latency.
Future NextImageLoaded;
Future LargeImageLoaded;
element.onMouseDown.forEach((_) {
NextImageLoaded = asyncActionStub(
"load next image. Returns completed future if already loaded");
LargeImageLoaded = asyncActionStub(
"load large version of active image to show in fullscreen mode."
"Returns completed future if already loaded");
});
Stream<UIEvent> userInputs = element.onClick as Stream<UIEvent>;
userInputs.transform(new UIEventToUIAction()).forEach((action) {
switch (action) {
case UIAction.FULLSCREEN:
LargeImageLoaded.then( (_) =>asyncActionStub("fullscreen mode") )
.then((_) => print("'full screen' finished"));
break;
case UIAction.NEXT:
NextImageLoaded.then( (_) =>asyncActionStub("next image") )
.then((_) => print("'next image' finished"));
break;
default:
asyncActionStub("???");
}
});
}
final DivElement element = querySelector("#element");
final Random rng = new Random();
final Set performed = new Set();
/**
*[asyncActionStub] Pretends to asynchronously do something usefull.
* Also pretends to use cache.
*/
Future asyncActionStub(String msg) {
if (performed.contains(msg)) {
return new Future.delayed(const Duration(milliseconds: 0));
}
print(msg);
return new Future.delayed(
new Duration(milliseconds: rng.nextInt(300)),
() => performed.add(msg));
}

How do I access `this` from JavaScript via JS - Dart interop?

I need to access a JavaScript object's this from a Dart function. I'm effectively adding a new method to a JavaScript object, via Dart-JS interop. I need to access properties that are on the JavaScript object from the method defined in Dart.
The Callback constructor can pass the this from JavaScript. According to the API docs for Callback:
new Callback.many(Function f, {bool withThis: false})
new Callback.once(Function f, {bool withThis: false})
Here is an example:
Dart code:
import 'dart:html';
import 'package:js/js.dart' as js;
void main() {
var greeter = js.context['greeter'];
var msg = greeter['greet']('Bob');
greeter['doCoolStuff'] = new js.Callback.many(doCoolStuff, withThis: true);
}
doCoolStuff(jsThis) {
print(jsThis['msg']);
}
Notice the use of withThis: true when creating the Callback. The this from JavaScript is passed in as the first argument to the callback function. In this case, I give it a name of jsThis.
JavaScript code:
function Greeter() {
this.msg = 'hello';
var that = this;
document.getElementById('clickme').addEventListener('click', function() {
that.doCoolStuff();
});
}
Greeter.prototype.greet = function(name) {
return this.msg + ' ' + name;
}
var greeter = new Greeter();
document.getElementById('clickme').addEventListener('click', function() {
greeter.doCoolStuff(); // comes from Dart land
});

Bind knockout.js to a boolean JQuery Mobile flip switch toggle

i have a boolean value bound to a JQM flip switch toggle, but i'm not able to see it reacting to changes to the underlying observable.
This is my true/false observable:
ko.booleanObservable = function (initialValue) {
var _actual = ko.observable(initialValue);
var result = ko.computed({
read: function () {
var readValue = _actual().toString();
return readValue;
},
write: function (newValue) {
var parsedValue = (newValue === "true");
_actual(parsedValue);
}
});
return result;
};
Which is the best way to combine JQM flip switch toggle and Knockout?
jsFiddle here: http://jsfiddle.net/nmq7z/
Thanks in advance to all
UPDATED: with a better test case:
http://jsfiddle.net/FU7Nq/
I got it,
Thanks to kadumel which point out that my first piece of code was really bad.
Then, i switched from a computed observable to a custom binding, which seems to me a good solution:
ko.bindingHandlers.jqmFlip = {
init: function (element, valueAccessor) {
var result = ko.bindingHandlers.value.init.apply(this, arguments);
try {
$(element).slider("refresh");
} catch (x) {}
return result;
},
update: function (element, valueAccessor) {
ko.bindingHandlers.value.update.apply(this, arguments);
var value = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
try {
$(element).slider("refresh");
} catch (x) {}
}
};
<select name="select-ismale" id="select-ismale" data-bind="jqmFlip: isMale.formattedValue" data-role="slider">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
Here is the working Fiddle: http://jsfiddle.net/FU7Nq/1/
Hope this can help some other People to deal with the JQM Flip Switch Toggle.
The binding with a "true" boolean observable is realized through an extender: this is the meaning of isMale.formattedValue.
This very clean and powerful solution is described in Tim's blog (thank You, Tim!).
Two things of note -
When you are making the checked value dependent on something I believe you need to use value: binding instead of checked: binding.
Second - You are setting it equal to a string of 'true' instead of boolean true, but your binding is to a boolean of true.
Try those adjustments in your binding and let me know if that doesn't fix it.
ko.bindingHandlers.jqmBindFlipSwitch = {
init: function (element, valueAccessor) {
$(element).change(function () {
var value = valueAccessor();
value($(element).is(":checked"));
}).blur(function () {
var value = valueAccessor();
value($(element).is(":checked"));
});
},
update: function (element, valueAccessor) {
var value = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
$(element).prop('checked', valueUnwrapped)
.flipswitch().flipswitch('refresh');
}
};
<input data-bind="jqmBindFlipSwitch: siteVisitRequired" type="checkbox" data-on-text="Yes" data-off-text="No" />
This seems to work fairly cleanly

Resources