Is there an event delegation in dart SDK? - dart

Imagine, you want to listen to all clicks made to any anchor element on page. The anchors on page can be dynamically added/removed during the page lifetime and you want to register all of the click events, even on newly added.
Is there any way how to attach delegated event (like in jQuery) in Dart using its standard libraries?
In jQuery you can achieve this with element.on('click', '.selector', handler);.

You can now do that with ElementStream.matches like this :
document.body.onClick.matches('.selector').listen((Event event) {
print('Super cool!');
// The currently registered target for the event
// document.body
event.currentTarget;
// The element whose CSS selector matched
// .selector
event.matchingTarget;
// The target to which the event was originally dispatched
// the real element clicked under .selector
event.target;
});

Because I have found no viable solution, I have created package that enables delegation.
You can find it at
http://pub.dartlang.org/packages/delegate
Code is simple:
delegate(parent, condition, handler) {
return (event) {
var element = event.target;
while (element != parent && element != null) {
if (condition(element)) {
handler(event, element);
}
element = element.parent;
}
};
}
delegateOn(parent, String eventType, condition, handler) {
parent.on[eventType].listen(delegate(parent, condition, handler));
}

Related

How to wait for routing to finish?

When a user clicks on a link, it uses routing to send the user to another component, you start at home and can click to charts. My problem is that I need to get the queryselector for the charts element, but when code is loaded from the component, it doesn't exist yet. How can I wait for the element to be created to execute the code?
I solved this by adding a MutationObserver inside the constructor.
Element chartsElement;
ChartsComponent() {
MutationObserver observer = MutationObserver(_onMutation);
Element my_app = querySelector('my-app');
observer.observe(my_app, childList: true);
}
_onMutation(List<dynamic> mutations, MutationObserver observer) {
mutations.forEach((value) {
MutationRecord record = value as MutationRecord;
if (record.addedNodes.contains('charts')) {
chartsElement = record.addedNodes[0];
}
});
observer.disconnect();
//Do stuff
}

Flipswitch in lightswitch is going in an infinite loop

I got this piece of code for rendering and using Flipswitch as a custom control in lightswitch application.
function createBooleanSwitch(element, contentItem, trueText, falseText, optionalWidth) {
var $defaultWidth = '5.4em';
var $defaultFalseText = 'False';
var $defaultTrueText = 'False';
var $selectElement = $('<select data-role="slider"></select>').appendTo($(element));
if (falseText != null) {
$('<option value="false">' + falseText + '</option>').appendTo($selectElement);
}
else {
$('<option value="false">' + $defaultFalseText + '</option>').appendTo($selectElement);
}
if (trueText != null) {
$('<option value="true">' + trueText + '</option>').appendTo($selectElement);
}
else {
$('<option value="true">' + $defaultTrueText + '</option>').appendTo($selectElement);
}
// Now, after jQueryMobile has had a chance to process the
// new DOM addition, perform our own post-processing:
$(element).one('slideinit', function () {
var $flipSwitch = $('select', $(element));
// Set the initial value (using helper function below):
setFlipSwitchValue(contentItem.value);
// If the content item changes (perhaps due to another control being
// bound to the same content item, or if a change occurs programmatically),
// update the visual representation of the control:
contentItem.dataBind('value', setFlipSwitchValue);
// Conversely, whenver the user adjusts the flip-switch visually,
// update the underlying content item:
$flipSwitch.change(function () {
contentItem.value = ($flipSwitch.val() === 'true');
});
// To set the width of the slider to something different than the default,
// need to adjust the *generated* div that gets created right next to
// the original select element. DOM Explorer (F12 tools) is a big help here.
if (optionalWidth != null) {
$('.ui-slider-switch', $(element)).css('width', optionalWidth);
}
else {
$('.ui-slider-switch', $(element)).css('width', defaultWidth);
}
//===============================================================//
// Helper function to set the value of the flip-switch
// (used both during initialization, and for data-binding)
function setFlipSwitchValue(value) {
$flipSwitch.val((value) ? 'true' : 'false');
// Having updated the DOM value, refresh the visual representation as well
// (required for a slider control, as per jQueryMobile's documentation)
$flipSwitch.slider(); // Initializes the slider
$flipSwitch.slider('refresh');
// Because the flip switch has no concept of a "null" value
// (or anything other than true/false), ensure that the
// contentItem's value is in sync with the visual representation
contentItem.value = ($flipSwitch.val() === 'true');
}
});
}
This piece of code works fine. It renders the flipswitch on the screen. I am showing the data in an Edit screen, which is coming in a popup. Problem arises when I open that popup which contains the flipswitch and without changing any data on UI, I just try to close that popup screen. The IE hangs and it gives error saying that long script is running. When I debug the createBoolenaSwitch function, I came to know that it is going in infinite loop inside the function called setFlipSwitchValue(value){}
Why is this function getting called and this is going in an infinite loop?

caret position in an editable div (dartlang)

I try to find the caret position in an editable div.
Also it should be nice to get the selected text in this div.
I try to assimilate this:
Editable Div Caret Position
But it dosen’t work.
I'll be happy to give any Idea.
Thanks in advance
Some code snippet
HTML
<a on-click="{{chooseMenu}}" which-menu="1">Menu 1</a>
Dart
void chooseMenu(Event event, var detail, var target) {
event.preventDefault();
event.stopPropagation();
Selection sel;
Range range;
sel = window.getSelection();
if(sel != null) {
range = sel.getRangeAt(0);
range.collapse(false);
}
currentSubMenu = int.parse(target.attributes['which-menu']);
}
This worked for me in Dartium.
The other code from the referenced SO question is probably to support other browsers.
Dart usually generates JS code that works in all supported browsers.
You have to test yourself if the generated JS really works in the browsers you want to support.
EDIT
We store the selection whenever it changes. Maybe there are better events but I couldn't find one.
But it worked fine for me.
We insert the new node at the previously stored selection.
// must be accessible by getRange, insertNodeAfterSelection
DivElement editable;
Range range;
// store reference to our contenteditable element
editable = (document.querySelector('#editable') as DivElement);
// subscribe selection change events
document.onSelectionChange.listen(getRange);
void insertNodeAfterSelection(Node node) {
range.collapse(false);
range.insertNode(node);
}
void getRange(Event e) {
if(document.activeElement != editable) { // save only selection changes on our contenteditable
return;
}
print(e);
Selection sel;
sel = window.getSelection();
if(sel != null) {
range = sel.getRangeAt(0);
}
}
You have to decide yourself where you put those code parts. I added some comments as support.
EDIT
using the mousedown event instead of click should help around this issue
Preserve text selection in contenteditable while interacting with jQuery UI Dialog and text input

jQuery UI – draggable 'snap' event

I'm looking a way to binding the snap event.
When I'm dragging an element over my surface and the draggable element is snapped to a declared snap position I want to trigger an event.
Something like this:
$(".drag").draggable({
snap: ".grid",
snaped: function( event, ui ) {}
});
Bonus point: with a reference to the .grid element where the draggable element was snapped.
The draggable widget does not expose such an event out of the box (yet). You could modify it and maintain your custom version or, better, derive a new widget from it and implement the new event there. There is, however, a third way.
From this question, we know the widget stores an array of the potentially "snappable" elements in its snapElements property. In turn, each element in this array exposes a snapping property that is true if the draggable helper is currently snapped to this element and false otherwise (the helper can snap to several elements at the same time).
The snapElements array is updated for every drag event, so it is always up-to-date in drag handlers. From there, we only have to obtain the draggable widget instance from the associated element with data(), and call its _trigger() method to raise our own snapped event (actually dragsnapped under the hood). In passing, we can $.extend() the ui object with a jQuery object wrapping the snapped element:
$(".drag").draggable({
drag: function(event, ui) {
var draggable = $(this).data("draggable");
$.each(draggable.snapElements, function(index, element) {
if (element.snapping) {
draggable._trigger("snapped", event, $.extend({}, ui, {
snapElement: $(element.item)
}));
}
});
},
snap: ".grid",
snapped: function(event, ui) {
// Do something with 'ui.snapElement'...
}
});
The code above, however, can still be improved. As it stands, a snapped event will be triggered for every drag event (which occurs a lot) as long as the draggable helper remains snapped to an element. In addition, no event is triggered when snapping ends, which is not very practical, and detracts from the convention for such events to occur in pairs (snapped-in, snapped-out).
Luckily, the snapElements array is persistent, so we can use it to store state. We can add a snappingKnown property to each array element in order to track that we already have triggered a snapped event for that element. Moreover, we can use it to detect that an element has been snapped out since the last call and react accordingly.
Note that rather than introducing another snapped-out event, the code below chooses to pass an additional snapping property (reflecting the element's current state) in the ui object (which is, of course, only a matter of preference):
$(".drag").draggable({
drag: function(event, ui) {
var draggable = $(this).data("draggable");
$.each(draggable.snapElements, function(index, element) {
ui = $.extend({}, ui, {
snapElement: $(element.item),
snapping: element.snapping
});
if (element.snapping) {
if (!element.snappingKnown) {
element.snappingKnown = true;
draggable._trigger("snapped", event, ui);
}
} else if (element.snappingKnown) {
element.snappingKnown = false;
draggable._trigger("snapped", event, ui);
}
});
},
snap: ".grid",
snapped: function(event, ui) {
// Do something with 'ui.snapElement' and 'ui.snapping'...
var snapper = ui.snapElement.attr("id"),snapperPos = ui.snapElement.position(),
snappee = ui.helper.attr("id"), snappeePos = ui.helper.position(),
snapping = ui.snapping;
// ...
}
});
You can test this solution here.
In closing, another improvement might be to make the snapped event cancelable, as the drag event is. To achieve that, we would have to return false from our drag handler if one of the calls to _trigger() returns false. You may want to think twice before implementing this, though, as canceling a drag operation on snap-in or snap-out does not look like a very user-friendly feature in the general case.
Update: From jQuery UI 1.9 onwards, the data() key becomes the widget's fully qualified name, with dots replaced by dashes. Accordingly, the code used above to obtain the widget instance becomes:
var draggable = $(this).data("ui-draggable");
Instead of:
var draggable = $(this).data("draggable");
Using the unqualified name is still supported in 1.9 but is deprecated, and support will be dropped in 1.10.
In jquery-ui 1.10.0, the above code doesn't work. The drag function is instead:
drag: function(event, ui) {
var draggable = $(this).data("ui-draggable")
$.each(draggable.snapElements, function(index, element) {
if(element.snapping) {
draggable._trigger("snapped", event, $.extend({}, ui, {
snapElement: $(element.item)
}));
}
});
}

jQuery Droppable element - conditional drop event based on item dropped

I have a problem that I can't seem to figure out. I'm trying to have a droppable element conditionally fire a different function based on the class of the item dropped. For the life of me I can't figure out how to do this. Here's the link: http://jsfiddle.net/643PC/22/
The pageContainer accepts Rows. Rows accept Spans. Spans should accept Actions and Fields and fire a different function based on which item is dropped. Any ideas?
Finished function with David's help:
function generalDrop(event, ui) {
var appendTarget = $(this);
if (ui.draggable.hasClass('field-item')) {
fieldDrop(event, ui, appendTarget);
}
else {
actionDrop(event, ui, appendTarget);
}
}
function actionDrop(event, ui, appendTarget) {
$(document.createElement('a'))
.addClass('btn btn-primary')
.attr('href', '#')
.text('Button')
.appendTo(appendTarget)
}
Change your generalDrop function to this:
function generalDrop(event, ui) {
if (ui.draggable.hasClass('field-item')) {
fieldDrop(event, ui);
}
else {
actionDrop(event, ui);
}
}

Resources