I have a simple application that binds to a view model using Knockout JS. It uses a foreach loop that fires the Knockout afterAdd event when a new item is added to the view model. The result is supposed to be a Kendo draggable that can be dropped on a target. For some reason I can't get the drop event on the target to fire.
JSFiddle
<button data-bind="click: $root.add">Add</button>
Drop target
var ViewModel = function () {
this.operations = ko.observableArray([]);
this.add = function () {
this.operations.push("drag");
}.bind(this);
this.bind = function () {
$(".draggable").kendoDraggable({
hint: function (e) {
$("#console").append("<li>firing hint</li>");
return e.clone();
},
});
$(".droptarget").kendoDropTarget({
drop: function (e) {
$("#console").append("<li>firing drop</li>");
}
});
};
};
ko.applyBindings(new ViewModel());
The problem is that you're instantiating the KendoDropTarget widget multiple times. If I click the Add button in your example kendoDropTarget() is invoked three times. If I add a guard against this (see http://jsfiddle.net/tj_vantoll/rk6qwsy4/1/) the drop event works as expected.
Related
I'm having some trouble understanding how event dispatching and binding to events between children parents work in the awesomeness that is Marionette.
Is it correct that I can trigger a custom event from an itemView like this:
var Item = Marionette.ItemView.extend({
events: {
"click .foo": "do:something"
}
});
var itemCollection = Marionette.CollectionView.extend({
itemView: item,
initialize: function () {
this.on("itemview:do:something", this.onSomething, this);
}
}};
Is there some shortcut to binding to the itemView events like I would DOM events:
var itemCollection = Marionette.CollectionView.extend({
itemView: item,
itemviewevents: {
"itemview:do:something": "onSomething"
}
}};
Thanks :).
You're confusing triggers and events. Your code should be
var Item = Marionette.ItemView.extend({
triggers: {
"click .foo": "do:something"
}
});
Use the events hash to have a function called when an event takes place, use the triggers hash to have a trigger executed.
I searched info on this topic but found only info about getting event element.
Yes, I can get an element of clicked div, but why it's fired all 19 times? (it's the number of views total). Info of clicked event is same - of the clicked div.
Here is what divs look like: http://d.pr/i/AbJP
Here is console.log: http://d.pr/i/zncs
Here is the code of index.js
$(function () {
var Table = new Backbone.Collection;
var TrModel = Backbone.Model.extend({
defaults: {
id: '0',
name: 'defaultName'
},
initialize: function () {
this.view = new Tr({model: this, collection: Table});
this.on('destroy', function () {
this.view.remove();
})
}
});
var Tr = Backbone.View.extend({
el: $('.pop-tags').find('.container'),
template: _.template($('#td_template').html()),
events: {
'click .tag': 'clicked'
},
clicked: function (event) {
event.preventDefault();
event.stopPropagation();
console.log(event.currentTarget);
},
initialize: function () {
this.render();
},
render: function () {
this.$el.append(this.template(this.model.attributes));
return this;
}
});
for (var i = 0, size = 19; i < size; i++) {
var trModel = new TrModel({id: i, name: 'name_' + i});
Table.add(trModel);
}
});
How can I avoid all elements from firing an event and fire only one that clicked and only 1 time?
el: $('.pop-tags').find('.container'),
Don't do that. You are attaching every view instance to the same DOM node. Each backbone view needs a distinct DOM node or, as you see, delegate events become complete chaos. In your view, set tagName: 'tr', then when creating your views, create them, call .render() and then append them to the DOM with something like $('.table-where-views-go').append(trView.el);.
You also may want to brush up on the basic MVC concept because Tables and Rows are view-related notions, not model-related, so a class called TrModel is a code smell that you aren't clear on Model vs View.
I would use a slightly different approach to solve your problem.
Instead for one view for every tr I would create one view for the entire table.
When I create the view I would pass the collection containing the 19 models to the view and in view.initialize use the collection to render the rows.
I created a jsbin with a working example.
When my ASP MVC 3 page loads, the user may or may not have a value selected from a group of radio buttons. If, when the page loads, a value is selected (before the user clicks on a button), how would i activate a jquery function?
Currently I use a .change() method to activate some code which decides what menu to display:
$(document).ready(function () {
$('input[name=TransactionType]').change(function () {
//Clear out values
$('input:text').val('');
$('input:text').text('');
//Display input fields
var radioValue = $(this);
$('#RightDiv').children().each(function () {
if (radioValue.attr('id') == $(this).attr('id')) {
$(this).show();
} else {
$(this).hide();
}
});
});
});
Something like that should work :
$('input[name=TransactionType]').change(function () {
//Your code
}).filter(':checked').trigger('change');
I am asuming the input are checkbox's, if that's not the case, you can remove the .filter().
Trigger doc : http://api.jquery.com/trigger/
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!
I need to avoid the double click submitting behavior. I'm using the client validation with the unobtrusive library. I have the following code for avoiding the double clic:
jQuery.fn.preventDoubleSubmit = function () {
var alreadySubmitted = false;
return jQuery(this).submit(function () {
if (alreadySubmitted)
return false;
else {
alreadySubmitted = true;
}
});
};
jQuery('form').preventDoubleSubmit();
Unfortunately, if my form has some validable fields (for example, a required field), the code above is still being fired, hence, even if I correct any mistakes on the form, I won't be able to submit it again.
How can I fire the double click code after the validation has been succesfully done?
You can also use the JQuery One event.
I have found that I could get past most guards against double-clicks by double-clicking fast. Using the one event is the only true way to make sure the event is only fired once. I don't think this technique will work "out of the box" with an input type=submit tag. Instead, you can simply use an input type=button or JQueryUI's .button().
$("#submitButton").one("click", function(event) {
$('#theForm').submit();
});
If you need to re-wire the event on a validation error (or other circumstance), I recommend that you create a function for the event handler. The function isn't necessary in this example because all the event handler does is submit the form, but in more complicated scenarios you may want to avoid repeating yourself.
function submitClick(event) {
$('#theForm').submit();
}
$("#submitButton").one('click', function(event) {
submitClick(event);
});
// This handler will re-wire the event when the form is invalid.
$('#theForm').submit(function(event) {
if (!$(this).valid()) {
event.preventDefault();
$('#submitButton').one('click', function(event) { submitClick(event); });
}
});
You could obviously add the disabling code here if you wanted to give feedback to the user that the button doesn't work anymore. One great side-effect of using the One event is that you don't actually have to make the button disabled, you can use a style of your own.
function submitClick(event) {
$('#submitButton').addClass('disabledButton');
$('#theForm').submit();
}
$("#submitButton").one('click', function(event) {
submitClick(event);
});
// This handler will re-wire the event when the form is invalid.
$('#theForm').submit(function(event) {
if (!$(this).valid()) {
event.preventDefault();
$('#submitButton').one('click', function(event) { submitClick(event); });
$('#submitButton').removeClass('disabledButton');
}
});
JQuery One Event: http://api.jquery.com/one/
I solved it with the following code:
var tryNumber = 0;
jQuery('input[type=submit]').click(function (event) {
var self = $(this);
if (self.closest('form').valid()) {
if (tryNumber > 0) {
tryNumber++;
alert('Your form has been already submited. wait please');
return false;
}
else {
tryNumber++;
}
};
});
NOTE: You can also replace the:
return false;
line, for:
self.attr('disabled', true);
BUT, if you use the name of your submit buttons on your controller for extra logic, they will be sent as null. (you can use an additional hidden field to charge them before submitting)
that's it, hope it helps
Rodrigo
EDIT: Thanks to these posts:
jquery newbie: combine validate with hidding submit button
Why not just use:
function disableButtons() {
var form = $(this);
var btns = $("input:submit", form);
if (!form.valid()) {
// allow user to correct validation errors and re-submit
btns.removeAttr("disabled");
} else {
btns.attr("disabled", "disabled");
}
}
to disable your buttons and activate it using:
$("form").bind("submit", disableButtons);
Based on Ryan P's popular answer I created the following generic solution that also works with my ajax form.
decorate your custom submit button with the following class:
<button type="button" class="one-click-submit-button">Submit</button>
Add the following to your javascript file:
function OneClickSubmitButton() {
$('.one-click-submit-button').each(function () {
var $theButton = $(this);
var $theForm = $theButton.closest('form');
//hide the button and submit the form
function tieButtonToForm() {
$theButton.one('click', function () {
$theButton.hide();
$theForm.submit();
});
}
tieButtonToForm();
// This handler will re-wire the event when the form is invalid.
$theForm.submit(function (event) {
if (!$(this).valid()) {
$theButton.show();
event.preventDefault();
tieButtonToForm();
}
});
});
}
OneClickSubmitButton();
since this is an ajax form we want to reload the handlers if we fail server validation.
function MyForm_OnSuccess() {
if (true if your form passed validation logic) {
//do something since your form submitted successfully
} else { //validation failed on server
OneClickSubmitButton(); //reinitialize the button logic
}
}
Obviously if you don't have ajax forms you can omit the whole OneClickSubmitButton function business and run $('.one-click-submit-button').each(... directly.
I have a form that uses MVC3 unobtrusive validation, and a viewmodel with a [RemoteAttribute].
It looks to me like the form's submit event only fires after all validation has passed. I'm currently using this, and it seems to work:
<input type="submit" value="Submit the Form"
data-app-disable-on-submit="true" />
$('form').live('submit', function() {
$(this).find('input[type="submit"][data-app-disable-on-submit="true"]')
.attr('disabled', 'disabled');
})
;
I set breakpoints on both the remote attribute validation action method and the HttpPost action method. Clicking the submit button the first time hits the breakpoint on the validation action method. At this point, the button is still enabled. I can click it multiple times, and after resuming the validation method, the HttpPost is hit only once. When the HttpPost is hit, the submit button is disabled.
Update
Right you are Alex. So an updated version of the above would look like this:
$('form').on('submit', function() {
$(this).find('input[type="submit"][data-app-disable-on-submit="true"]')
.attr('disabled', 'disabled');
})
$('form').submit(function () {
$('input[type="submit"]', this).attr('disabled', 'disabled');
});
I use a different approach to this. Not wiring to the click event of the button, but to the submit event of the form. Works like a charm to prevent multiple simultaneous submits of forms.
function initFormsToPreventSimultaneousSubmits(selector) {
if (!selector) {
selector = 'form'; // No selector supplied, apply to all forms on the page
}
// Make sure all forms that conform to selector are marked as not submitting
$(selector).each(function()
{
var $form = $(this);
$form.data('submitting', false);
});
// Attach to submit event of all forms that conform to selector
$(selector).off('submit').on('submit', function (e) {
var $form = $(this);
if (!$form.valid || $form.valid()) { // Make sure to only process when the form is valid or jquery validation is not used
if ($form.data('submitting')) {
// form is already submitting. Classic case of double click on one of the submit buttons of the form. Stop the submit
e.preventDefault();
return false;
} else {
// All ok, mark the form as submitting and let the form perform the submit
$form.data('submitting', true);
return true;
}
}
});
}
On document ready i call initFormsToPreventSimultaneousSubmits() to init all forms on the page.
Only thing to remember is that when u use a ajax form post is to call the initFormsToPreventSimultaneousSubmits('#formId') on the OnComplete event of the AjaxOptions settings. Because otherwise the form will still be marked as submitting when its done. When a 'normal' form post is used this is not an issue.
Extends answers by Alex and Ryan P to accounts for situations where jQuery Validation might be missing and where multiple submit buttons exist in a single form.
oneClickSubmitButton = function () {
$('input[type=submit], button[type=submit], input[type=image]').each(function () {
var $theButton = $(this);
var $theForm = $theButton.closest('form');
//hide the button and submit the form
function tieButtonToForm() {
$theButton.one('click', function () {
$theButton.addClass('ui-state-disabled');
});
}
tieButtonToForm();
$theForm.submit(function (event) {
// Only proceed for the clicked button
if (!$theButton.hasClass("ui-state-disabled"))
return;
// If jQuery Validation is not present or the form is valid, the form is valid
if (!$theForm.valid || $theForm.valid())
return;
// Re-wire the event
$theButton.removeClass('ui-state-disabled');
event.preventDefault();
tieButtonToForm();
});
});
};
I was able to fix a similar issue with a couple of lines of code. I prefer this if you don't want to "alert" to user that they double clicked and just silently ignore the second click.
I just made a global javascript variable that I toggled when my function was executing during a critical section. This kept subsequent function calls from re-executing the same section.
var criticalSection = false;
SomeOnClickEventFired = function () {
if (!criticalSection)
{
criticalSection = true;
//Ajax Time
criticalSection = false;
}
}