emberjs: how to trigger a custom event in a View - events

I would like to turn a primitive event (a click) into a semantic event, like "deleteTodo"
This is described here, but not how to implement :(
I have the following code:
App.TodoView = Em.View.extend({
click: function(e) {
this.trigger("deleteTodo");
}
});
App.Router.map(function(match) {
match('/').to('index');
});
App.IndexRoute = Ember.Route.extend({
deleteTodo: function(e) {
// this code is never executed :(
}
}) ;
After I perform the 'click', I see that the TodoView click function is called, but not the deleteTodo function from the IndexRoute. Any suggestions what might go wrong here ?
CHeers

You can use this.get("controller").send("deleteTodo"). This will send a message to the controller, if the controller doesn't handle deleteTodo it will bubble to the router and be handled there.
click: function(e) {
this.get('controller').send("deleteTodo");
}
In your router you will also need to define the event:
events: {
doStuff: function(e) {
alert("Do stuff") ;
}
}
http://jsfiddle.net/9Xasr/7/
I would typically do record deletion in the controller. Seems like putting that in a router event would not be ideal.

Related

Kendo scheduler month View double click function

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 {
}
}

How to unbind an external event when a controller/$scope is destroyed?

Supposed I have a controller for a page that is being rendered into an ng-view.
This controller binds to some events of an external source (such as an application-wide message bus) to update its model. This basically works very easy:
function MyController ($scope) {
$scope.bar = '…';
externalSource.on('foo', function (data) {
$scope.$apply(function () {
$scope.bar = data.bar;
});
});
}
The problem is: How do I unbind the controller from the external source once the view it it is associated with is not longer shown?
Is there something such as a dispose event or something similar?
Or is my approach completely wrong, and I should deal with something like that somehow else? If so, how?
To execute event unbind when controller's scope is got destroyed use:
$scope.$on('$destroy', function () { /* Unbind code here */ });
See Scope docs for more info
Use $routeChangeStart or $routeChangeSuccess events:
function MyController ($scope) {
$scope.bar = '…';
externalSource.on('foo', function (data) {
$scope.$apply(function () {
$scope.bar = data.bar;
});
});
$scope.$on('$routeChangeStart', function(next, current){
// unregister listener
// externalSource.off ....
});
}
... or $destroy event:
$scope.$on('$destroy', function(){
// unregister listener
// externalSource.off ....
});

How to use events and event handlers inside a jquery plugin?

I'm triyng to build a simple animation jQuery-plugin. The main idea is to take an element and manipulate it in some way repeatedly in a fixed intervall which would be the fps of the animation.
I wanted to accomplish this through events. Instead of using loops like for() or while() I want to repeat certain actions through triggering events. The idea behind this: I eventualy want to be able to call multiple actions on certain events, like starting a second animation when the first is done, or even starting it when one animation-sequence is on a certain frame.
Now I tried the following (very simplified version of the plugin):
(function($) {
$.fn.animation = function() {
obj = this;
pause = 1000 / 12; //-> 12fps
function setup(o) {
o.doSomething().trigger('allSetUp');
}
function doStep(o, dt) {
o.doSomething().delay(dt).trigger('stepDone');
}
function sequenceFinished(o) {
o.trigger('startOver');
}
function checkProgress(o) {
o.on({
'allSetup': function(event) {
console.log(event); //check event
doStep(o, pause);
},
'stepDone': function(event) {
console.log(event); //check event
doStep(o, pause);
},
'startOver': function(event) {
console.log(event); //check event
resetAll(o);
}
});
}
function resetAll(o) {
/*<-
reset stuff here
->*/
//then start over again
setup(o);
}
return this.each(function() {
setup(obj);
checkProgress(obj);
});
};
})(jQuery);
Then i call the animation like this:
$(document).ready(function() {
$('#object').animation();
});
And then – nothing happens. No events get fired. My question: why? Is it not possible to use events like this inside of a jQuery plugin? Do I have to trigger them 'manualy' in $(document).ready() (what I would not prefer, because it would be a completely different thing – controling the animation from outside the plugin. Instead I would like to use the events inside the plugin to have a certain level of 'self-control' inside the plugin).
I feel like I'm missing some fundamental thing about custom events (note: I'm still quite new to this) and how to use them...
Thx for any help.
SOLUTION:
The event handling and triggering actually works, I just had to call the checkProgress function first:
Instead of
return this.each(function() {
setup(obj);
checkProgress(obj);
});
I had to do this:
return this.each(function() {
checkProgress(obj);
setup(obj);
});
So the event listening function has to be called before any event gets triggered, what of course makes perfect sense...
You need set event on your DOM model for instance:
$('#foo').bind('custom', function(event, param1, param2) {
alert('My trigger')
});
$('#foo').on('click', function(){ $(this).trigger('custom');});​
You DOM element should know when he should fire your trigger.
Please note that in your plugin you don't call any internal function - ONLY DECLARATION

ExtJS 4.1 Call One Controller From Another

Note: I'm a total ignoramus regarding javascript.
I've broken my ExtJS 4.1 MVC app out into several controllers like:
/app/controller/Auth
| |Quiz
| |Result
| |Blah...
|model/...
I want to respond to an "event", not a DOM Event, rather a Ext.form.action.Submit.success event by calling functions in both my Auth and Quiz controllers. The summarized code for the first part is here:
// File: app/controller/Auth.js
attemptLogin : function() {
var form = Ext.ComponentQuery.query('#loginpanel')[0].form;
if (form.isValid()) {
form.submit({
success : function(form, action) {
// THIS IS THE FUNCTION FROM THE CURRENT CONTROLLER
Assessor.controller.Auth.prototype.finishLogin();
// THIS IS THE FUNCTION FROM THE OTHER CONTROLLER
Assessor.controller.Quiz.prototype.setupAssessment();
},
This works but feels wrong. Is there a proper way to do this? It seems like I should fire a unique event that is listened to by both controllers, but I can't understand how to do that with Ext.Event. Any guidance?
Thanks! I'm really grateful for all the great ideas and advice.
It makes sense to me to fire a custom event from the form and simply listen to it in both your controllers, like what you said here:
It seems like I should fire a unique event that is listened to by both
controllers
// File: app/controller/Auth.js
attemptLogin : function() {
var form = Ext.ComponentQuery.down('#loginpanel').form;
if (form.isValid()) {
form.submit({
success : function(form, action) {
// fire the event from the form panel
form.owner.fireEvent('loginsuccess', form.owner);
},
Then in each of your controllers you can listen to it with Controller#control, like this:
Ext.define('YourApp.controller.Auth', {
extend: 'Ext.app.Controller',
init: function() {
var me = this;
me.control({
'#loginpanel': {
loginsuccess: me.someHandler
}
});
},
someHandler: function(form) {
//whatever needs to be done
console.log(form);
}
}
And then add the same thing to your Quiz controller:
Ext.define('YourApp.controller.Quiz', {
extend: 'Ext.app.Controller',
init: function() {
var me = this;
me.control({
'#loginpanel': {
loginsuccess: me.someOtherHandler
}
});
},
someOtherHandler: function(form) {
//whatever needs to be done
console.log(form);
}
}
I've used this approach successfully in 4.1.0 and 4.1.1
It really should be
Assessor.controller.Auth.prototype.finishLogin.apply(this, arguments)
or something along these lines (in order to have a correct this reference that points to the 'owner' of the method, the controller object)
However, why do you use this unorthodox way to call the current controller's method. Just set the scope for the success callback, then call this.finishLogin().
form.submit({
success : function(form, action) {
// THIS IS THE FUNCTION FROM THE CURRENT CONTROLLER
this.finishLogin();
...
},
scope: this
});
Also, you can retrieve another controller instance using Controller#getController.
this.getController('Assessor.controller.quiz').setupAssignment();
Then, if your controller methods are not depending on each other, you could make them both listen to the same event.
Another solution is to fire a custom event once the login is finished. You could do that on the application object
this.application.fireEvent('logincomplete');
and in your controller's init method:
this.application.mon('logincomplete', this.setupAssignment, this);
Please note that you cannot listen to those events via Controller#control - see Alexander Tokarev's blog post for a patch to Ext to achieve this.
There is no standard way to fire events between controllers, but it's possible with some custom hacks. See my recent blog post.
I have also been looking for this and all you need is Asanda.app.getController('quiz').setupAssignment();, where Asanda is the name of your app
You should use a MessageBus if you have to send events between controllers:
Ext.define('MyApp.utils.MessageBus', {
extend : 'Ext.util.Observable'
});
store the message bus in a global var
MsgBus = Ext.create('MyApp.utils.MessageBus');
Where you have to send events:
MsgBus.fireEvent('eventName',eventArg_1,eventArg_2);
Where you have to receive events:
MsgBus.on('eventName', functionHandler,scope); //scope is not mandatory
...
functionHandler:function(eventArg_1,eventArg_2){
...
//do whatever you want
...
}

ASP.NET MVC - How to prevent double click submit with jquery.validate.unobtrusive lib?

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;
}
}

Resources