jquery Select2 prevent selecting in ajax response - javascript

I want to prevent from adding a category to the Select2 element if it fails creating the row first in my db. The action is not prevented when i call ev.preventDefault(); Nothing happens.. what is wrong?
$('#sel2').select2({
placeholder: 'Enter categories',
minimumInputLength: 3,
multiple: true,
ajax: {
url: 'async/get_categories.php',
dataType: 'json',
quietMillis: 250,
data: function (term, page) {
return {
q: term,
};
},
results: function (data, page) {
return {
results: data.items
};
},
cache: true
},
formatResult: format,
formatSelection: format
}).on('select2-selecting', function(e) {
console.log(e);
if (e.val == 4) {
// if category id equals 4
// do not add this category to select 2
// e.preventDefault();
// the above works just fine and its just for testing
}
// Is something wrong here?
var ev = e;
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: ev.val
},
success: function(response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
ev.preventDefault();
// the above is not working
}
},
error: function() {
alert('Failed to assign category!');
}
});
});

The AJAX request is made asynchronusly, so by the time it has finished the element has already been added. Even though you are calling ev.preventDefault(), it is too late for it to make a difference. So this leaves you with two options:
Make the request synchronusly, which will allow preventDefault to make the difference.
Make the request asynchronusly, and manually remove the element if it fails.
Both options have their pros and cons, and it's up to you to decide which option you go with.
Making the request synchronusly
Pros
The value will never be added if the request fails.
Works well in cases where the element cannot be added quite often.
Cons
Blocks the UI - So the user is potentially left with an unresponsive page while the request is made.
Making the request asynchronusly
Pros
Does not block the UI.
Works well in cases where elements typically can be added.
Cons
The value will always show up for the user, even if it fails later.
You must manually unset the new option.
What's important to consider here is the user experience of both options. When making synchronus requests, it's not uncommon for the browser to stop relaying events - which gives the illusion that the UI has locked up and the page has gone unresponsive. This has the benefit of ensuring that the value never shows up if it isn't allowed. But if users typically can add the elements, it also has the downside of complicating the most common use case.
If users can usually add elements, then it is a better experience to add the element while the request is being made, and then notifying the user later (while removing the element) if there was an issue. This is very common is web applications, and you can see it being used in many places, such as the Twitter and Facebook like buttons (where requests usually work), as well as places on Stack Overflow.

There is a way to get around this with version4 of the select2 library.
on select2:selecting we cancel the preTrigger event. Which will stop the select2:select event. We do our ajax call. On success we then get out Select2 instance then call the trigger of the Observer that way it by passes overwritten trigger method on your select2 instance.
The call method needs your select2 instance as the context so that the existing listeners are available to call.
var sel = $('#sel');
sel.select2(config);
sel.on('select2:selecting', onSelecting);
function onSelecting(event)
{
$.ajax({
type: 'POST',
url: 'async/create_profile_category.php',
data: {
profile_id: '1',
category_id: event.params.args.data.id
},
success: function(event, response) {
console.log(response);
if (response.error === false) {
// category assigned successfully
// get select2 instance
var Select2 = $users.data('select2');
// remove prevented flag
delete event.params.args.prevented;
// Call trigger on the observer with select2 instance as context
Select2.constructor.__super__.trigger.call(Select2, 'select', event.params.args);
} else {
// failed to assign category
// so i want now to prevent from adding to select2
console.log('should not add this category');
}
}.bind(null, event),
error: function() {
alert('Failed to assign category!');
}
});
event.preventDefault();
return false;
}

here how I did it for yii2 Select2 integrated into Gridview:
'pluginEvents' => [
'select2:selecting' => "
function(event)
{
var select2 = $('#types-" . $model->id . "');
select2.select2('close');
$.post('update',{id: " . $model->id . ", type_id: event.params.args.data.id})
.done (function(response)
{
select2.val(event.params.args.data.id);
select2.trigger('change');
})
.fail(function(response)
{
krajeeDialog.alert('Error on update:'+response.responseText);
});
event.preventDefault();
return false;
}",
],
it allows to asynchoronous update data in the grid using select2 and ajax and return it to previous value if there was an error on updating.

Related

How do you Get and Add events to a specific source?

I was successfully able to specify multiple sources: My SQL server source, and an empty array to hold any user-generated events userAddedEvents = []. The idea was that I could use this array later to "save" new events to my server.
var userAddedEvents = []; //container for event objects that will hold user-generated content
var calendar = new FullCalendar.Calendar(calendarEl, {
eventSources: [
{
events: function (fetchInfo, successCallback, failureCallback) {
$.ajax({
url: url,
type: 'post',
contentType: "application/json; charset=utf-8", //include fetchInfo in request body instead of form-data
dataType: "json",
data: JSON.stringify(fetchInfo), //the fetchInfo object must be stringified
success: function (data) {
events = $.merge([], data.events);
successCallback(events);
}
});
}
}, //end server source
{
id: 'userAddedEvents',
events: userAddedEvents
} //end local array source
]
});
So, here's me trying to add an event to my userAddedEvents source...
select: function (info) {
// https://fullcalendar.io/docs/select-callback
console.log(info)
console.log('selected ' + info.startStr + ' to ' + info.endStr)
calendar.addEvent({
start: info.startStr,
end: info.endStr,
rendering: 'background', //v4 and v5 use different terminology
color: '#ff9f89',
selected: true, //custom, extended property
}, userAddedEvents);
calendar.unselect(); //clear the current selection
},
Anyway, long story short... when I try to dump out the results of userAddedEvents, it's empty, although, I do see new content on my calendar.
==UPDATED== with example included... I added a custom button where I attempt to get content from the userAddedEvents array.
Alternatively (also shown in the example), I've had success getting ALL events with calendar.getEvents(), then using $.grep to filter some specific property or extended property. Ultimately though, I suppose I'm trying to use a "temporary event source" for the sake of convenience -- I can act upon the userAddedEvents array, stringify it, empty it, etc. I do not know how to getEvents for a specific source object.
customButtons: {
getUserCreatedEvents: {
text: 'Get User Events',
click: function () {
console.log(calendar.getEventSources()); //both event sources are listed
console.log(calendar.getEventSourceById(userAddedEvents)); //missing the id in output {calendar, id, internalEventSource, url}
console.log(calendar.getEventSourceById('userAddedEvents')); //has the the id in output {calendar, id, internalEventSource, url}
console.log(userAddedEvents) //the array is empty
/*
events = calendar.getEvents();
// console.log(events)
var filteredResultsGREP = $.grep(events, function (event) {
return event.rendering === 'background';
});
*/
/*
https://fullcalendar.io/docs/Event-toPlainObject (version 5 only)
*/
// this WILL show any events added based on the property specified
// console.log(filteredResultsGREP);
}
}
}
How do I get the events that are new? I want to hold all user-created events before I send them to SQL for processing.
Firstly, apologies for totally forgetting about this question, having started to help with it.
After some digging it looks like you can't get the raw events for an individual event source, which is a bit annoying.
So I think actually your simplest approach is just to add the events to your separate array, without worrying about the structure in fullCalendar. Then you can just send that list to the server when you want to save them.
select: function (info) {
var evt = {
start: info.startStr,
end: info.endStr,
rendering: "background", //v4 and v5 use different terminology
color: "#ff9f89",
selected: true //custom, extended property
};
calendar.addEvent(evt);
userAddedEvents.push(evt);
calendar.unselect(); //clear the current selection
},
Demo: https://codepen.io/ADyson82/pen/abdVVNM
The only extra complication you might have is if you allow events to be dragged or resized (or otherwise edited) after they've been added, you'll have to sync that with the separate array, which is a bit of extra work.

'book' textfield does not trigger event in sencha

I have this code Ext.get('book').setValue('1');
Note: Loads the page and book value is set to 1. Not after page load
and book value change to 1.
It sets the book to value 1. But it does not trigger a change event. Is there a way to trigger the change event after page loads?
Edit:
In html script,
<script..>
$(document).ready(function () {
$("book").on("blur", function() {
//calls other function
}); // not called as blur is not invoked
});
</script>
<input id="book" type="book" value="" /><br />
In extjs,
var panel = Ext.create('Ext.grid.Panel', {
id: 'panel',
columns: [
var bookid = "new book";
Ext.Ajax.request({
params: { bookid: bookid},
function: function (response) {
Ext.get('book').setValue(bookid);
// after setValue, book will receive a change event(e.g .blur in html) and changes other functions
}
});
]
});
Your ajax request seems to be malformed, the function: function statement would be the place where you put normally success: function like in the following statement:
Ext.Ajax.request({
url: 'insert-your-http-endpoint-here',
params: {
bookid: bookid
},
success: function(response){
debugger; // -> setting this statement will show that you enter the success statement
Ext.get('book').setValue(bookid);
},
failure: function(response, opts) {
// something went wrong with your request
console.log('server-side failure with status code ' + response.status);
}
});
more info about how to use ExtJS or the specific function, you could find in the documentation (check if you have the correct version, ofcourse) which can be found here
From the above code, you don't need the debugger statement, but it could help if you want to check if you actually get into this code block or not, and what happens when you try to set the value.
Also, don't forget to check your console output when something is not working, maybe there was a problem that would be clearly indicated in the console log

Add extra data in textextjs plugin

I'm trying to call a funtion in textextjs method-
//my function
function GetAreaTags() {
return "some text";
}
//textext initializtion
$('#territory').textext({
plugins: 'tags prompt focus autocomplete ajax',
ajax: {
url: '/Admin/Search/GetTerritorySuggestions?area=' + GetAreaTags(),
dataType: 'json',
cacheResults: false
}
});
But GetAreaTags() is not being called. How can i make it happen?
It should work... But try this:
function GetAreaTags() {
return "some text";
};
var yourObj= {
plugins: 'tags prompt focus autocomplete ajax',
ajax : {
url: '/Admin/Search/GetTerritorySuggestions?area=' + GetAreaTags(),
dataType: 'json',
cacheResults: false
}
};
$('#territory').textext(yourObj);
console.log(yourObj.ajax.url);
If that doesn't work out try this:
function GetAreaTags() {
return "some text";
};
var yourObj= {
plugins: 'tags prompt focus autocomplete ajax',
ajax : {
url: function() {return '/Admin/Search/GetTerritorySuggestions?area=' + GetAreaTags()},
dataType: 'json',
cacheResults: false
}
};
$('#territory').textext(yourObj);
console.log(yourObj.ajax.url);
Check the console both times to see if your url is what you desire.
[EDIT: I rejected the edit by mistake, sorry about that]
Edit2
From s.k.paul's comment:
GetAreaTags() should execute every time i type in that textbox.
However, console says- 1. /Admin/Search/GetTerritorySuggestions?area=
2. localhost:12788/Admin/Dashboard/…}&q= 404 (Not Found)
Therefore you need another event handler to dynamically change the url (the plugin must be recalled with another url):
function GetAreaTags() {
return "some text";
};
$("#territory").keyup(function() {
var yourObj= {
plugins: 'tags prompt focus autocomplete ajax',
ajax : {
url: function() {return '/Admin/Search/GetTerritorySuggestions?area=' + GetAreaTags()},
dataType: 'json',
cacheResults: false
}
};
$('#territory').textext(yourObj);
console.log(yourObj.ajax.url);
});
However, this may be very heavy... The plugin expects you to have a single reference for your auto-complete resource. If you're dynamically changing it, it may reset the already existing stuff.
Edit3
Edit 2 : textextjs does not work at all now. And, url function returns
whole function text
This means the plugin doesn't handle well being recalled twice or more times in the same element. The only possible solution I am seeing is to change the plugin's code in order to dynamically change the resources according to your function...
Which makes me wonder, if it's easier for you to allow the user to have a broader data resource (include all areas) when typing, this way there would be only one URL and the plugin wouldn't have any trouble with that.

jQuery: How to apply a function to all elements including some which are loaded later via Ajax?

I have a simple jQuery function that resizes text areas, and I want it to apply to all text areas.
For the most part, this works great:
$(document.ready(function(){$("text_area").resizer('250px')});
However, because it is only called once when the document is ready, it fails to catch text areas that are later added onto the page using Ajax. I looked at the .live() function, which seems very close to what I'm looking. However, .live() must be bound to a specific event, whereas I just need this to fire once when they're done loading (the onLoad event doesn't work for individual elements).
The only thing I can get working is a really obtrusive inclusion of the JavaScript call directly into the Ajax. Is that the recommended way to be doing this?
Edit: Here is the rails source code for what it does for Ajax requests:
$('a[data-confirm], a[data-method], a[data-remote]').live('click.rails', function(e) {
var link = $(this);
if (!allowAction(link)) return false;
if (link.attr('data-remote') != undefined) {
handleRemote(link);
return false;
} else if (link.attr('data-method')) {
handleMethod(link);
return false;
}
});
// Submits "remote" forms and links with ajax
function handleRemote(element) {
var method, url, data,
dataType = element.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
if (element.is('form')) {
method = element.attr('method');
url = element.attr('action');
data = element.serializeArray();
// memoized value from clicked submit button
var button = element.data('ujs:submit-button');
if (button) {
data.push(button);
element.data('ujs:submit-button', null);
}
} else {
method = element.attr('data-method');
url = element.attr('href');
data = null;
}
$.ajax({
url: url, type: method || 'GET', data: data, dataType: dataType,
// stopping the "ajax:beforeSend" event will cancel the ajax request
beforeSend: function(xhr, settings) {
if (settings.dataType === undefined) {
xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
}
return fire(element, 'ajax:beforeSend', [xhr, settings]);
},
success: function(data, status, xhr) {
element.trigger('ajax:success', [data, status, xhr]);
},
complete: function(xhr, status) {
element.trigger('ajax:complete', [xhr, status]);
},
error: function(xhr, status, error) {
element.trigger('ajax:error', [xhr, status, error]);
}
});
}
So in my particular case, I've got a link, that has data-remote set to true, which points to a location that will return JavaScript instructing a form containing a text area to be appended to my document.
A simple way to do this would be to use ajaxComplete, which is fired after every AJAX request:
$(document).ajaxComplete(function() {
$('textarea:not(.processed)').resizer('250px');
});
That says "every time an AJAX request completes, find all textarea elements that don't have the processed class (which seems to be added by the resizer plugin -- terrible name for its purpose!) and call the resizer plugin on them.
You may be able to optimise this further if we could see your AJAX call.
Generally speaking, I would do it this way..
$.ajax({
type : "GET",
url : "/loadstuff",
success: function(responseHtml) {
var div = $("#containerDiv").append(responseHtml);
$("textarea", div).resizer("250px");
}
});
Wondering if you could use .load for this. For example:
$('text_area').load(function() {
$("text_area").resizer('250px');
});

How can I determine/use $(this) in js callback script

I am using Rails and jQuery, making an ajax call initiated by clicking a link. I setup my application.js file to look like the one proposed here and it works great. The problem I'm having is how can I use $(this) in my say.. update.js.erb file to represent the link I clicked? I don't want to have to assign an ID to every one, then recompile that id in the callback script..
EDIT
To give a simple example of something similar to what I'm trying to do (and much easier to explain): If a user clicks on a link, that deletes that element from a list, the controller would handle the callback, and the callback (which is in question here) would delete the element I clicked on, so in the callback delete.js.erb would just say $(this).fadeOut(); This is why I want to use $(this) so that I dont have to assign an ID to every element (which would be the end of the world, just more verbose markup)
application.js
jQuery.ajaxSetup({ 'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript,application/javascript,text/html")} })
function _ajax_request(url, data, callback, type, method) {
if (jQuery.isFunction(data)) {
callback = data;
data = {};
}
return jQuery.ajax({
type: method,
url: url,
data: data,
success: callback,
dataType: type
});
}
jQuery.extend({
put: function(url, data, callback, type) {
return _ajax_request(url, data, callback, type, 'PUT');
},
delete_: function(url, data, callback, type) {
return _ajax_request(url, data, callback, type, 'DELETE');
}
});
jQuery.fn.submitWithAjax = function() {
this.unbind('submit', false);
this.submit(function() {
$.post(this.action, $(this).serialize(), null, "script");
return false;
})
return this;
};
// Send data via get if <acronym title="JavaScript">JS</acronym> enabled
jQuery.fn.getWithAjax = function() {
this.unbind('click', false);
this.click(function() {
$.get($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
// Send data via Post if <acronym title="JavaScript">JS</acronym> enabled
jQuery.fn.postWithAjax = function() {
this.unbind('click', false);
this.click(function() {
$.post($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
jQuery.fn.putWithAjax = function() {
this.unbind('click', false);
this.click(function() {
$.put($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
jQuery.fn.deleteWithAjax = function() {
this.removeAttr('onclick');
this.unbind('click', false);
this.click(function() {
$.delete_($(this).attr("href"), $(this).serialize(), null, "script");
return false;
})
return this;
};
// This will "ajaxify" the links
function ajaxLinks(){
$('.ajaxForm').submitWithAjax();
$('a.get').getWithAjax();
$('a.post').postWithAjax();
$('a.put').putWithAjax();
$('a.delete').deleteWithAjax();
}
show.html.erb
<%= link_to 'Link Title', article_path(a, :sentiment => Article::Sentiment['Neutral']), :class => 'put' %>
The combination of the two things will call update.js.erb in rails, the code in that file is used as the callback of the ajax ($.put in this case)
update.js.erb
// user feedback
$("#notice").html('<%= flash[:notice] %>');
// update the background color
$(this OR e.target).attr("color", "red");
jQuery already handles the this issue for you with the event properties:
$("a").click(function(e){
e.preventDefault();
$("#foo").fadeIn(3000, function(){
$(e.target).text("Foo loaded");
});
});​​​
Note how I can refer back to the main link via its event. This is the case with any events that are handled within as well. Just give them unique names, such as e2, e3, etc. No more need to constantly write yet another var item = $(this) line to keep track of this three events back.
Online Demo: http://jsbin.com/egelu3/edit
If your JS is coming from the server, there is really no way that $(this) can operate in the same context. The closest you could get would be to load some script from the server and eval it in the context of your client-side function.
I basically have an id for each of the DOM elements I need to manipulate, and refer to them within my scripts. It is occasionally ugly, but the alternatives are worse.
If your JS is coming from the server,
there is really no way that $(this)
can operate in the same context. The
closest you could get would be to load
some script from the server and eval
it in the context of your client-side
function.
Not true
I basically have an id for each of the
DOM elements I need to manipulate, and
refer to them within my scripts. It is
occasionally ugly, but the
alternatives are worse.
I don't think this is ugly.
The key to this problem is functional scoping. Let me show you what I mean. You need to create a function that is called before you send your XHR call. In your case, you're doing it with a click event, so let me show you an example tailored for you:
$( '#somelink' ).click( function( )
{
// this stores the location of the current "this" value
// into this function, and will available even after we
// end this function (and will still live while the XHR's
// callback is being executed
var theLink = this;
// fire the AJAX call
$.post
(
'some/url',
{ 'some':'data' }, // optional
function( )
{
// use theLink however you want
// it'll still be there
// also, if you're sending in callbacks
// as variables, you can safely say
hideAndStore.call( theLink, data );
// which executes callback( ), overriding
// "this" with theLink (your DOM node)
// and sending in the responseText as the
// first argument
}
);
} );
and then you could make your callback something like:
function hideAndStore( response )
{
// you can safely use "this" as the DOM node
$( this ).css( { 'display':'none' } );
// and you can do whatever you wish with the response
window.globalData = response;
}
where you'd make it do whatever you actually want it to do, haha.
For more info about functions in JavaScript that change the "this" value, check out .apply and .call at MDC
https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Function/Apply https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Function/Call
What are you doing in the javascript you are sending back? Maybe you can send back some html or json and operate on it in a callback.
$('a:clickable').bind('click', function() {
var elem = $(this);
$.ajax({
url: ...,
dataType: 'json',
success: function(data) {
// elem is still in scope
elem.addClass(data.class_to_add_to_link);
}
});
});
This cannot be accomplished, The method in which i am trying to do this makes it impossible, i cannot pass references to javascript objects through views.
Solution was to assign IDs to each item, and refer to them by that.

Categories

Resources