The task is the same as in
this post: capture click on a link and prevent the browser's default behavior. The answer given there was this:
Template:
<a on-click='sayHello' href="#">Activate!</button>
JS:
ractive.on( 'sayHello', function ( event ) {
event.original.preventDefault();
alert( 'Hello world!' );
});
The problem is that the sayHello event handler should not know about what the original event was. The whole point of proxying is that the event handler should not care about the type of the original event. For example, I change the link into a button, there's no more need for preventDefault.
So, my question is how I can invoke preventDefault before or after firing the event proxy.
A nice solution would be to fire several event proxies in a row (if it was possible):
Template:
<a on-click='preventDefault;sayHello' href="#">Activate!</button>
Js:
ractive.on( 'preventDefault', function ( event ) {
event.original.preventDefault();
});
ractive.on( 'sayHello', function ( event ) {
alert( 'Hello world!' );
});
Is this possible somehow? Or is there some other nice solution?
One approach might be to pass an argument that determines whether the default should be prevented:
template:
<a on-click='sayHello:true' href="#">Activate!</a>
<button on-click='sayHello'>Activate!</button>
code:
ractive.on( 'sayHello', function ( event, shouldPreventDefault ) {
if ( shouldPreventDefault ) {
event.original.preventDefault();
}
alert( 'Hello world!' );
});
Or, if you wanted to avoid the boolean trap you could do...
<a on-click='sayHello:{preventDefault:true}' href="#">Activate!</a>
...et cetera.
Chaining events is an interesting idea. I'm not convinced it belongs in the library, but you could certainly do something like
template:
<a on-click='multiple:["preventDefault","sayHello"]' href='#'>Activate!</a>
code:
ractive.on( 'multiple', function ( event, sequence ) {
var eventName, i, len;
len = sequence.length;
for ( i = 0; i < len; i += 1 ) {
this.fire( sequence[i], event );
}
});
This pattern could be expanded upon to include arguments if needed.
I solved it this way, but I'm not totally satisfied.
Template:
<a on-click='pd_and:sayHello' href="#">Activate!</button>
Js:
ractive.on( 'pd_and', function ( event, next ) {
event.original.preventDefault();
var i = args.indexOf(':'), arg;
if (i === -1) {
type = next;
arg = undefined;
} else {
type = next.substr(0, i);
arg = next.substr(i + 1);
}
return ractive.fire(type, event, arg);
});
If your goal is to encapsulate the preventDefault between template and method, then you can roll your own proxy event:
Ractive.events.clickpd = function ( node, fire ) {
node.addEventListener('click', click)
function click(e){
e.preventDefault()
fire({
node: node, //breaks Ractive if not included
original: e //optional
})
}
return {
teardown: function () {
node.removeEventListener('click', click)
}
}
}
And use it like so:
<a on-clickpd='sayGreeting'>{{greeting}}</a>
See http://jsfiddle.net/ka2Pu/2/ for full example.
Docs on custom events are here: http://docs.ractivejs.org/latest/ractive-events
Related
I disabled the built-in pop up event. Now I want to implement a double click function on each cell of the month view.
Does anyone know how to do it?
You can add an event handler to the add event of the scheduler in the scheduler options like this:
add: (e) => {
// Place your code here.
e.preventDefault();
}
or in case you would rather not use arrow function:
add: function(e) {
// Place your code here.
e.preventDefault();
}
Calling e.preventDefault() will disable the built-in "add" event handling which is showing the popup window. You mentioned you already disabled it but this is a good way to do it if you did it in another way.
e will contain the slot's start and end time as well as the resource details, if you use resources.
You may want to associate the event with k-event class of an scheduler.
$("#scheduler").on("dblclick", '.k-event', function (e) {
var scheduler = $("#scheduler").getKendoScheduler();
var element = $(e.target).is(".k-event") ? $(e.target) : $(e.target).closest(".k-event");
var event = scheduler.occurrenceByUid(element.data("uid"));
alert("Start Date : " + event.start + ", End Date: " + event.end);
});
Demo Link
Try this it worked for me.
edit: function (e) {
e.preventDefault(); //prevent popup editing
var dataSource = this.dataSource;
var event = e.event;
if (event.isNew()) {
setTimeout(function () {
//dataSource.add(event);
editEvent(event); // your own function to call
});
}
else {
}
}
In a Meteor app, one function that runs on a click takes a while to run on some slower devices. As a result, on these slow devices, the app doesn't seem to do anything until after the function has completed.
The function in question loops through a largish array. It does not do any external stuff, or method calls.
To visually clue in the user, I want to show a spinner to the user (on slow devices). Ideally, I would show the spinner at the start of the event, and then remove the spinner at the end of the event. However, if my understanding of Meteor is correct (and based on what seems to be happening when I try it out), all template updates specified during the event are only propagated at the end of the event. So, as a result, the spinner never shows.
How can I make this work as intended?
Current setup (edit with actual code):
In categories.html:
{{#if hasSubCategories}}
<a href='#_' id='categorySelect-{{id}}' class='btn categorySelect pull-right
{{#if allSubsSelected}}
btn-danger
{{else}}
btn-success
{{/if}}
'>{{#if isFlipping}}<span id='spinner-{{id}}'>flip</span>{{/if}}<span class="glyphicon
{{#if allSubsSelected}}
glyphicon-remove
{{else}}
glyphicon-ok
{{/if}}
" aria-hidden="true"></span></a>
{{/if}}
In categories.js:
Template.categories.events({
"click .categorySelect": function (event) {
Session.set('categorySpinner', this.id);
categoryFlipper(this.id, function() {
Session.set('categorySpinner', "");
});
return false;
},
});
Template.categories.helpers({
allSubsSelected: function() {
var finder = Categories.find({parentId: this.id});
var allSelected = true;
finder.forEach(function(item) {
if (!($.inArray(item.id, Session.get("categoriesSelected")) !== -1)) {
allSelected = false;
}
});
return allSelected;
},
isFlipping: function() {
if (Session.get("categorySpinner") == this.id)
return true;
else
return false;
}
});
In main.js:
categoryFlipper = function (id, callback) {
var finder = Categories.find({parentId: id});
var allSelected = true;
finder.forEach(function(item) {
if (!($.inArray(item.id, Session.get("categoriesSelected")) !== -1)) {
allSelected = false;
}
});
var t = Session.get("categoriesSelected");
if (allSelected) {
finder.forEach(function(item) {
t.splice($.inArray(item.id, t), 1);
});
}
else {
finder.forEach(function(item) {
if (!($.inArray(item.id, t) !== -1)) {
t.push(item.id);
}
});
}
Session.set("categoriesSelected", t);
callback();
}
Looking at your sample code (though I still don't have a complete picture since I don't see the helper definitions for allSubsSelected and isFlipping), I'm pretty sure that your categoryFlipper function executes so quickly that you never really see the spinner. There's nothing in that code that would really take any significant amount of time. You have a find() call, but that's not what really takes the most amount of time. It's your subscribe() call that you usually need to figure in some delay for as the data is pulled from the database to the mini-mongo client in the browser.
Something like this is fairly common:
{{#unless Template.subscriptionsReady}}
spinner here...
{{else}}
Content here.
{{/unless}}
That way, while your subscription is triggering the publication and pulling data down, Template.subscriptionsReady returns false, and the spinner is shown. Try that approach instead.
This has been asked and answered several times, yet I keep comparing my syntax to the proper syntax as described in the answeres, and can't find what's wrong.
$('.voteBtn').bind('click', function () {
var vote = 1;
$.post(
"vote.php",
{ vote: vote},
function(vote) {
alert (vote); // BUG HERE
}
);
});
My expected result is an alert of '1', as defined at var vote = 1;
But the current result is an alert of a blank string.
What am I doing wrong?
you have a parameter called vote which is causing the problem, so inside the ajax callback vote variable is referring to the data returned by the ajax request, not the closure variable in the click handler
To fix the problem rename the parameter to something else like data
$('.voteBtn').bind('click', function () {
var vote = 1;
$.post("vote.php", {
vote: vote,
pollID: id
}, function (data) {
alert(vote); // BUG HERE
});
});
Currently, vote that you alert is the data returned from your ajax request, not the one you defined before, try to use this instead:
$('.voteBtn').bind('click', function () {
var $vote = 1;
$.post(
"vote.php",
{ vote: $vote},
function(vote) {
alert ($vote); // BUG HERE
}
);
});
I've built a dynamic menu which also have highlighting. Now i got a problem, number of path-change events are increasing along with menu elements.
Of course, that's the result of applying directive on each element of the menu.
Custom directives at the moment is my most weak place, and i don't have idea how to refactor all this.
I also made an attempt to put directive in root element of menu (ul) in order to register watch once, but stuck on accessing the deep children elements (ul->li->a.href).
Here's the directive:
app.directive("testdir", function($location)
{
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
scope.$watch(function() { return $location.path(); }, function(path)
{
scope.$parent.firedEvents++;
path=path.substring(1);
if(path === element.children().attr("href").substring(2))
{
element.addClass("activeLink");
}
else
{
element.removeClass("activeLink");
}
})
}
};
And HTML Part:
<ul ng-app="test" ng-controller="MenuCtrl">
<li ng-repeat="link in menuDef" testdir>{{link.linkName}}</li>
</ul>
Whole example on JsFiddle
How this can be refactored? I'm exhausted.
and, i'm moving in the right direction? I have a feeling that this thing could be done in bit easier way, but maybe i'm wrong.
First of all, your firedEvents means how many time the callback has been called, not how many times the location actually changed, it's not the "number of path-change events"!
You have 20 (as in your fiddle) scopes are watching the location change, when you click on a different link other than the current active one, the location changes, ALL of the 20 scopes will see the change and call their own $watch callback functions, and each call to the callback will increase your firedEvents, so the result is what you have seen: the count goes up by 20.
Therefore, if you want to make the firedEvents to count how many time location has changed, you should move scope.$parent.firedEvents++; into the if. But keep in mind that every click will still cause the callback function be called by 20 times!
There are many ways to achieve the same effect you're trying to do here, I have a solution for you without digging into directive at all. Here you go:
HTML
<ul ng-controller="MenuCtrl">
<li ng-repeat="link in menuDef" ng-class="{activeLink: link.isActive}" ng-click="onLinkClick(link)">{{link.linkName}}
</li>
</ul>
JS
app.controller("MenuCtrl", function ($scope, $location) {
var menugen = [];
for (var i = 1; i <= 20; i++) {
menugen.push({
linkName: "Link " + i,
url: "#/url" + i
});
}
$scope.menuDef = menugen;
var activeLink = null;
$scope.onLinkClick = function (link) {
if (activeLink && $scope.activeLink !== link) {
activeLink.isActive = false;
}
link.isActive = true;
activeLink = link;
};
});
jsFiddle
Update
My first attempt is targeting simplicity, but as #VirtualVoid pointed out, it has a huge drawback -- it can't easily handle location change from outside of the menu.
Here, I came up a better solution: adding a directive to the ul, watch location change in there, and update activeLink in the callback function of the watch. In this way, one $watch is called, and only one callback will be called for a click.
JS
app.directive('menu', function ($location) {
return {
restrict: 'A',
controller: function ($scope, $location) {
var links = [];
this.registerLink = function (elem, path) {
links.push({
elem: elem,
path: path
});
};
$scope.$watch(function () {
return $location.path();
}, function (path) {
for (var i = 0; i < links.length; i++) {
if (path === links[i].path) {
links[i].elem.addClass('activeLink');
} else {
links[i].elem.removeClass('activeLink');
}
}
});
}
};
}).
directive("testdir", function () {
return {
restrict: 'A',
require: '^menu',
link: function (scope, element, attrs, controller) {
controller.registerLink(element, scope.link.url.substring(1));
}
};
});
HTML
<ul ng-app="test" ng-controller="MenuCtrl" menu>
<li ng-repeat="link in menuDef" testdir>
{{link.linkName}}
</li>
</ul>
jsFiddle: http://jsfiddle.net/jaux/MFYCX/
I'm trying to make a special menu pop up in Slick Grid when the user holds down the mouse button for some amount of time. The menu has to relate specifically to that column, so I need to somehow retrieve the information from that column. Since the popup will appear during the time the mouse button is held, I can't use an onclick event.
I do have some code I used to make this work for the main body of the grid:
// Attach the mousedown and mouseup functions to the entire grid
$(document).ready(function () {
$("#myGrid").bind("mousedown", function (e, args) {
var event = e;
timer = setTimeout(function () { heldMouse(event, args); }, 500);
})
.bind("mouseup", function () {
clearTimeout(timer);
});
});
function heldMouse(e, args) {
// if header click -
// showHeaderContextMenu(e, args)
// else
showContextMenu(e);
mouseHeldDown = false;
}
// Displays context menu
function showContextMenu(e) {
var cell = grid.getCellFromEvent(e);
grid.setActiveCell(cell.row, cell.cell);
$("#slickGridContextMenu")
.css("top", e.pageY - 5)
.css("left", e.pageX - 5)
.show();
}
(I know args is going to be empty, it's there more as a placeholder than anything else for now)
This works perfectly fine for holding down on the main body, but if I do this on the header, since I no longer have access to getCellFromEvent and the mouse events I've called can't get args from anywhere, I'm unsure how I can get information about the header and which column I'm in. Is there a bit of functionality in Slickgrid that I've overlooked? I didn't notice a mousedown event I could subscribe to.
I had a thought that I could bind separate events to both the header and the body, but I'm not sure if that would still be able to get me the information I need. Looking at the tags, I see two possibilities: slick-header-column, which has an id (slickgrid_192969DealNbr) but I'm not sure how to reliably remove that number (where does it come from?), or slick-column-name, where I can get the html from that tag, but sometimes those names are altered and I'd rather not deal with that unless I absolutely have to. This is what I'm referring to:
<DIV id=slickgrid_192969OrdStartDate_ATG title="" class="ui-state-default slick-header-column" style="WIDTH: 74px">
<SPAN class=slick-column-name>
<SPAN style="COLOR: green">OrdStartDate</SPAN></SPAN><SPAN class=slick-sort-indicator></SPAN>
<DIV class=slick-resizable-handle></DIV></DIV>
Also, I have this:
grid.onHeaderContextMenu.subscribe(function (e, args) {
e.preventDefault();
// args.column - the column information
showHeaderContextMenu(e, args);
});
Is there a way perhaps I could trigger this event from the mouse holding methods and receive args with the column name in it? This was just a thought, I'm not really sure how the triggering works with SlickGrid/JQuery, but figured it might be possible.
Thanks for any suggestions!
Try something like this instead (demo):
$(function () {
var timer;
$("#myGrid")
.bind("mousedown", function (e, args) {
var event = e; // save event object
timer = setTimeout(function(){
doSomething(event, args);
}, 1000);
})
.bind("mouseup mousemove", function () {
clearTimeout(timer);
});
});
function doSomething(e, args){
alert('YAY!');
};
And in case you were wondering why I saved the e (event object), it is because of this (ref):
The event object is valid only for the duration of the event. After it returns you can't expect it to remain unchanged. In fact, the entire native event object (event.originalEvent) cannot be read in IE6/7/8 after the event has passed--it's gone and will throw an error if you try to access it. If you need an unchanging copy of some event data, make one.
I got something that works, for now, with the help of Mottie's idea (was not aware of "closest" in jquery). It's not elegant, but so be it.
Here's the structure of one of the column tags:
<DIV id=slickgrid_132593DealNbr title="" class="ui-state-default slick-header-column slick-header-column-sorted" style="WIDTH: 49px">
<SPAN class=slick-column-name>DealNbr</SPAN>
<SPAN class="slick-sort-indicator slick-sort-indicator-asc"></SPAN>
<DIV class=slick-resizable-handle></DIV></DIV>
And here are the calls made to get it.
$(document).ready(function () {
$("#myGrid").bind("mousedown", function (e) {
var event = e;
timer = setTimeout(function () { heldMouse(event); }, 500);
})
.bind("mouseup", function () {
clearTimeout(timer);
$("body").one("click", function () { cleanup(); });
});
});
function heldMouse(e) {
// If the click was not on one of the headers, it won't find this
if ($(e.target).closest('.slick-header-column').html() != null) {
var $foundColumnHeader = $(e.target).closest('.slick-header-column');
// columns being used to populate SlickGrid's columns
var held_col = columns[$foundColumnHeader.index()];
showHeaderContextMenu(e, { column: held_col });
}
else {
showContextMenu(e);
}
mouseHeldDown = false;
}
Thanks!