Bind Event for New Elements - javascript

How can I register an event for new elements programmatically? I'm using AutoComplete library and Data Table library (add row function) and the autocomplete event is only applied on the existing rows but not for new inserted rows. How can I fix that? Please help...
This is what I've tried...
$('#addRow').on('click', function () {
let input = document.createElement("input")
input.setAttribute("type", "text");
input.setAttribute("class", "material_no");
input.setAttribute("id", "material_no");
input.setAttribute("placeholder", "Enter material no.");
table.row.add( [
'-',
`
<input type='text' class='material_no' id = 'material_no' placeholder = 'Enter Material No.'/>
`,
'-',
'-',
'-',
'-',
'-',
'-',
'-',
'-',
'-',
] ).draw();
counter++;
} );
// Automatically add a first row of data
$('#addRow').click();
//autocomplete library
$('.material_no').each(function(i, e) {
$(this).bind('change').autocomplete({
source: '/mef/suggestions.php',
change: function( event, ui ) {
console.log($(this).val())
console.log(table.row($(this).parent()).index())
// $.ajax({
// url: '/mef/ajax/part_names.php',
// method: 'POST',
// data: { part_code: $(this).val()},
// success: data => {
// $(`td:eq(${i})`).html(data)
// console.log(data)
// },
// error: err => console.log(err)
// })
return false;
},
})
})

I solved it by putting event inside the draw function in datatable. I don't know if it is good approach or can cause bugs in the future...
table.on('draw', function () {
$('.material_no').each(function(i, e) {
$(this).autocomplete({
source: '/mef/suggestions.php',
change: function( event, ui ) {
console.log($(this).val())
console.log(table.row($(this).parent()).index())
// $.ajax({
// url: '/mef/ajax/part_names.php',
// method: 'POST',
// data: { part_code: $(this).val()},
// success: data => {
// $(`td:eq(${i})`).html(data)
// console.log(data)
// },
// error: err => console.log(err)
// })
return false;
},
})
})
});

Related

Problems to use ajaxstart and ajaxstop jquery

I have tried to use ajaxStart and ajaxStop to showing a loader as long as all functions load.
I consult all the urls and send a request to each of them, So until they do not load all do not hide the loader.
This is my code:
$.ajax({
url: getBaseUri() + 'dashboard/index',
method: 'GET',
success:function(data){
var datas = data['return'];
var urls = [];
var idDash = [];
var types = [];
/*
* I go through the data to send the data of each dashboard
*/
for (var i in datas) {
//Urls, dashboard ids and descriptions are saved in an array
urls.push({
url: datas[i].route,
id: datas[i].id,
title: datas[i].privilege,
div: datas[i].div
});
idDash.push(datas[i].id);
types.push(datas[i].type);
/*
* I send the parameters to the receiveData ()
*/
receiveData(datas[i].route, datas[i].sign, datas[i].class, datas[i].div, datas[i].privilege, datas[i].type, types, idDash, datas[i].id, urls, datas[i].label, datas[i].xaxis, datas[i].yaxis, datas[i].background);
}
},
error: function(error){
console.log(error);
}
});
function receiveData(url, sign, iconClass, div, title, type, types, idDash, id, urls, label, xaxis, yaxis, background) {
$.ajax({
url: url,
method: 'GET',
success: function(data){
var datas = data['return'];
if (type === "Bar") {
barChart(datas, title, div, type, types, idDash, id, urls);
}
if (type === "Indicator") {
indicatorsChart({
data: datas,
div: div,
title: title,
icon: sign,
class: iconClass,
idDash: id
});
}
if (type === "Sowing") {
sowingIndicator({
data: datas,
div: div,
title: title,
idDash: id
});
}
if (type === "BarChart") {
barCharts({
data: datas,
div: div,
title: title,
url: url,
label: label,
xaxis: xaxis,
yaxis: yaxis,
type: sign,
background: background
});
}
},
error: function(error){
console.log(error);
}
});
}
I have used this but it doesn´t work:
$(".loader").bind("ajaxStart", function () {
$(".loader").show();
}).bind("ajaxComplete", function () {
$(".loader").hide();
});
I don´t know if I am doing something wrong, I will be grateful for any help.
Thanks!
You're not binding to the correct element - you typically should bind ajaxStart to the document itself:
$(document).bind("ajaxStart", function () {
$(".loader").show();
}).bind("ajaxComplete", function () {
$(".loader").hide();
});
And a JSFiddle demonstrating this works:
http://jsfiddle.net/x196bb4L/1/
Can you try
$( document).ajaxStart(function () {
$(".loader").show();
});
$( document).ajaxComplete(function() {
$(".loader").hide();
});
Ref
You could show the loader at the time you trigger the ajax call, and hide it on ajax callback like this:
var loader = $('.loader');
loader.show();
$.ajax({
...
success: function(data) {
loader.hide();
},
error: function() {
loader.hide();
}
});

Yii2 activeform ajax submit and validation

Currently to achieve ajax submit and validation at the same time. I'm using custom function like:
$('.edit_form').submit(function (e) {
e.preventDefault();
var form = $(this);
var formData = $(this).serialize();
if (form.find('.has-error').length) {
return false;
}
$.ajax({
url: form.attr("action"),
type: form.attr("method"),
data: formData,
success: function (data) {
...
},
error: function () {
alert("Something went wrong");
}
});
});
And here is php side, for validation my config looks like that:
$form = ActiveForm::begin([
'id' => "some_form",
'action' => ['user/edit'],
'options' => ['class' => 'edit_form'],
'enableAjaxValidation' => false,
'enableClientValidation' => true,
]); ?>
I'm sure that It's not the best way to achieve what I need. Especially this part that I use for preventing for submission in case of validation error:
if (form.find('.has-error').length) {
return false;
}
Any suggestions? How to achieve ajax submission and validation properly using Yii 2's inbuilt settings?
Use beforeSubmit event instead of submit, the beforeSubmit will only be triggered once the form passes the validation.
$('form').on('beforeSubmit', function(e) {
var form = $(this);
var formData = form.serialize();
$.ajax({
url: form.attr("action"),
type: form.attr("method"),
data: formData,
success: function (data) {
...
},
error: function () {
alert("Something went wrong");
}
});
}).on('submit', function(e){
e.preventDefault();
});
you can use AjaxSubmitButton.
Your form should b
$form = ActiveForm::begin([
'id' => "some_form",
'action' => 'javascript:void(0)',
'options' => ['class' => 'edit_form'],
]);
use AjaxSubmitButton here
AjaxSubmitButton::begin([
'label' => 'Submit',
'id' => 'some_form',
'ajaxOptions' => [
'type' => 'POST',
'url' => \yii\helpers\Url::to(['/user/edit']),
'success' => new \yii\web\JsExpression(
'function(data){
if(data=="success")
{
}else{
$.each(data, function(key, val) {
$("#"+key).after("<div class=\"help-block\">"+val+"</div>");
$("#"+key).closest(".form-group").addClass("has-error");
});
}
}'
),
],
'options' => ['class' => 'btn btn-success', 'type' => 'submit'],
]);
AjaxSubmitButton::end();
In your controller
public function actionEdit()
{
$model = new User;
if($model->save())
{
$result = 'success';
Yii::$app->response->format = trim(Response::FORMAT_JSON);
return $result;
}else{
$error = \yii\widgets\ActiveForm::validate($model);
Yii::$app->response->format = trim(Response::FORMAT_JSON);
return $error;
}
}
Here I've find some interesting javascript-side validation tricks
So, a javascript submit button can be as:
$('body').on('click', '#submit', function(e) {
e.preventDefault();
var yiiform = $('#my-form');
$.ajax({
type: yiiform.attr('method'),
url: yiiform.attr('action'),
data: yiiform.serializeArray(),
success: function(data) {
if(data.success == 'true') {
window.location.href = 'http://my.success.page';
} else {
// here there is (maybe) the right way to trigger errors
$.each(data, function(key, val) {
yiiform.yiiActiveForm('updateAttribute', key, [val]);
});
}
}
});
}
triggered by:
<?= Button::widget([
'label' => Yii::t('app', 'Submit'),
'options' => [
'id'=>'submit',
'class' => 'btn btn-primary pull-right',
]]);?>
And the action controller will reply with:
...
if ($model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
if($model->save()) {
return ['success'=>'true'];
} else {
return ActiveForm::validate($model);
}
}
...
Details about ActiveForm::validate() can be found here

CKeditor populate dialog select with Ajax

I am trying to populate my CKeditor dialog selectbox with ajax. The following is from my plugin.js file:
...
{
type : 'select',
id : 'style',
label : 'Style',
setup : CKEDITOR.ajax.post( '.../ckeditor/plugins/simpleLink/ajax.php', JSON.stringify( { foo: 'bar' } ), 'application/json', function( data ) {
console.log( data);
}),
items : [ ['--- Select something ---', 0] ],
commit : function( data )
{
data.style = this.getValue();
}
}
...
The ajax output looks like this:
["Basketball","basketball"],["Baseball","baseball"],["Hockey","hockey"]
I am really wondering how to get the output INTO the "items". From my point of view I tried everything.
Can someone help me?
CKEditor select widgets have an 'add' function you can call to populate them. You can call it from an 'onLoad' function, if you add one:
{
type: 'select',
id: 'myselect',
label: 'The select will be empty until it is populated',
items: [ ],
onLoad: function(api) {
widget = this;
$.ajax({
type: 'GET',
url: 'path/to/your/json',
dataType: 'json',
success: function(data, textStatus, jqXHR) {
for (var i = 0; i < data.length; i++) {
widget.add(data[i]['label'], data[i]['value']);
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log('ajax error ' + textStatus + ' ' + errorThrown);
},
});
},
},
Found a workaround. For anyone having the same problem - here is my way of solving it:
plugin.js:
This code before "CKEDITOR.plugins.add( 'PLUGINNAME', {..."
jQuery.extend({
getValues: function(url) {
var result = null;
$.ajax({
url: url,
type: 'get',
dataType: 'json',
async: false,
success: function(data) {
result = data;
}
});
return result;
}
});
var results = $.getValues('.../ckeditor/plugins/PLUGINNAME/ajax.php');
This is the code for the select box
{
type : 'select',
id : 'style',
label : 'Style',
setup : '',
items : results,
commit : function( data )
{
data.style = this.getValue();
}
}

Loading remote data only once with Select2

As the title suggests I would like to load remote data once only.
I thought about loading a data with independent ajax call and set it "locally" at the control but wonder if there is more "built in" way to do so...
a solution can be found here:
https://github.com/ivaynberg/select2/issues/110
$("#selIUT").select2({
cacheDataSource: [],
placeholder: "Please enter the name",
query: function(query) {
self = this;
var key = query.term;
var cachedData = self.cacheDataSource[key];
if(cachedData) {
query.callback({results: cachedData.result});
return;
} else {
$.ajax({
url: '/ajax/suggest/',
data: { q : query.term },
dataType: 'json',
type: 'GET',
success: function(data) {
self.cacheDataSource[key] = data;
query.callback({results: data.result});
}
})
}
},
width: '250px',
formatResult: formatResult,
formatSelection: formatSelection,
dropdownCssClass: "bigdrop",
escapeMarkup: function (m) { return m; }
});
Edit:
I might have misinterpreted your question. if you wish to load all data once, then use that is Select2, there is no built in functionality to do that.
Your suggestion to do a single query, and then use that stored data in Select2 would be the way to go.
This is for Select2 v4.0.3:
I had this same question and got around it by triggering an AJAX call and using the data returned as the initialized data array.
// I used an onClick event to fire the AJAX, but this can be attached to any event.
// Ensure ajax call is done *ONCE* with the "one" method.
$('#mySelect').one('click', function(e) {
// Text to let user know data is being loaded for long requests.
$('#mySelect option:eq(0)').text('Data is being loaded...');
$.ajax({
type: 'POST',
url: '/RetrieveDropdownOptions',
data: {}, // Any data that is needed to pass to the controller
dataType: 'json',
success: function(returnedData) {
// Clear the notification text of the option.
$('#mySelect option:eq(0)').text('');
// Initialize the Select2 with the data returned from the AJAX.
$('#mySelect').select2({ data: returnedData });
// Open the Select2.
$('#mySelect').select2('open');
}
});
// Blur the select to register the text change of the option.
$(this).blur();
});
This worked well for what I had in mind. Hope this helps people searching with the same question.
To load data once:
Assumptions:
You have a REST API endpoint at /services that serves a JSON array of objects
The array contains objects which have at least a "name" and "id" attribute. Example:
[{"id": 0, "name": "Foo"}, {"id": 1, "name": "Bar"}]
You want to store that array as the global 'services_raw'
First, our function to load the data and create the global 'services_raw' (AKA 'window.services_raw'):
fetchFromAPI = function() {
console.log("fetchFromAPI called");
var jqxhr = $.ajax(
{
dataType:'json',
type: 'GET',
url: "/services",
success: function(data, textStatus, jqXHR) {
services_raw = data;
console.log("rosetta.fn.fetchServicesFromAPI SUCCESS");
rosetta.fn.refreshServicesSelect();
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("Error inside rosetta.fn.fetchServicesFromAPI", errorThrown, textStatus, jqXHR);
setTimeout(rosetta.fn.fetchServicesFromAPI(), 3000); // retry in 3 seconds
}
}
)
.done(function () {
console.log("success");
console.log(jqxhr);
})
.fail(function () {
console.log("error");
})
.always(function () {
console.log("complete");
});
// Perform other work here ...
// Set another completion function for the request above
jqxhr.always(function () {
console.log("second complete");
});
};
Second, our Select2 instantiation code which transforms our data into a format that Select2 can work with:
refreshServicesSelect = function () {
// ref: http://jsfiddle.net/RVnfn/2/
// ref2: http://jsfiddle.net/RVnfn/101/ # mine
// ref3: http://jsfiddle.net/RVnfn/102/ # also mine
console.log('refreshServicesSelect called');
$("#add-service-select-service").select2({
// allowClear: true
data: function() {
var arr = []; // container for the results we're returning to Select2 for display
for (var idx in services_raw) {
var item = services_raw[idx];
arr.push({
id: item.id,
text: item.name,
_raw: item // for convenience
});
}
return {results: arr};
}
});
};
Here's what the Select2 element in HTML should look like before your call the above functions:
<input id="add-service-select-service" type="hidden" style="width:100%">
To use all of this, call (in JS):
window.fetchFromAPI();
window.refreshServicesSelect();
Lastly, here's a JSFiddle where you can play with a similar thing: http://jsfiddle.net/RVnfn/102/
Basically, in my example above, we're just using ajax to populate the equivalent of window.pills in the Fiddle.
Hope this helps :)
Please reply if you know how to do this via the Select2 .ajax function, as that would be a bit shorter.
In my condition, it is working perfectly with the given code
$('#itemid').select2({
cacheDataSource: [],
closeOnSelect: true,
minimumInputLength: 3,
placeholder: "Search Barcode / Name",
query: function(query) {
// console.log(query);
self = this;
var key = query.term;
var cachedData = self.cacheDataSource[key];
if(cachedData) {
query.callback({results: cachedData});
return;
} else {
$.ajax({
url: "./includes/getItemSelect2.php",
data: { value : query.term },
dataType: 'json',
type: 'POST',
success: function(data) {
self.cacheDataSource[key] = data;
query.callback({results: data});
}
});
}
},
});
And my data return from the ajax is in this form
<?php
$arr = [
["id" => 1, "text" => "Testing"],
["id" => 2, "text" => "test2"],
["id" => 3, "text" => "test3"],
["id" => 4, "text" => "test4"],
["id" => 5, "text" => "test5"]
];
echo json_encode($arr);
exit();
?>

JavaScript is Doing a Post on Server Side Twice

Student Data Grid that display the student id and a description in the grid. It also has a select button when the user click on it that would route to a javascript function. This function will set some flags on the server side, close the window, past the studentID on a search box and do an automatic search on the studentID. The click appears to be doing exactly what I want it to do.
However, if the user were to double click on a row in the grid, it is supposed to do the exact same thing. it should also do a post. The double click is doing the post twice. What is causing it to do the post twice? I have not been able to figure it out. I've been putting alert all over the place and no success as to why.
In case you may be wondering why I have the dataroute and a client side script. This grid is in a pop up page that is also being called from other pages. When the user calls the grid from another page, the user will have the ability to select multiple records vs. only being able to select one records when they are calling it from the Course Page.
Here is the Grid:
#(Html
.Telerik()
.Grid((IEnumerable<OverrideStudent>)SessionWrapper.Student.OtherStudentSelected)
.Name("StudentData")
.DataKeys(Keys =>
{
Keys.Add(c => c.StudentID);
})
.DataBinding(databinding => databinding.Server())
.Columns(columns =>
{
columns.Bound(p => p.StudentId)
.Title("Student ID")
.Width(15)
.Sortable(true)
.Filterable(false);
columns.Bound(p => p.StudentDescription)
.Title("Description")
.Width(65)
.Sortable(true)
.Filterable(false);
columns.Command(command =>
{
command.Custom("AddStudent")
.Text("Select")
.DataRouteValues(routes =>
{
routes.Add(o => o.StudentID).RouteKey("StudentID");
routes.Add(o => o.StudentDescription).RouteKey("StudentDescription");
})
.Action("Student", "StudentInfo");
.HtmlAttributes(new { onclick = "PostData(this);StudentSelectClick(this)" });
}).Width(20);
}).ClientEvents(clients => clients
.OnComplete("OnComplete")
//.OnDataBinding("DataBinding")
//.OnDataBound("onRowDataBound")
.OnRowSelected("StudentDoubleClick")
)
.Sortable()
.Selectable()
.Filterable(filtering => filtering
.Enabled(true)
.Footer(true)
.HtmlAttributes(new { style = "padding-right: 0.0em;" }))
Here are the JavaScripts that are doing the post:
function StudentDoubleClick(e) {
var fromCourse = "#SessionWrapper.Student.FromCoursePage";
var fromList = "#SessionWrapper.Student.FromListingPage";
if (fromCourse == "True") {
$('tr', this).live('dblclick', function () {
alert("Inside TR count = " + count);
count = count + 1;
DoSearchStudent(e);
});
}
if (fromList == "True") {
$('tr', this).live('dblclick', function () {
DoSearchStudent(e);
});
}
}
function DoSearchStudent(e) {
var row = e.row;
var StudentID = row.cells[0].innerHTML;
var StudentDescription = row.cells[1].innerHTML;
// alert(procCodeDesc);
var data = { "StudentID": StudentID, "StudentDescription": StudentDescription, "action": "Double Click" };
var url = '#Url.Action("Student", "StudentInfo")';
$.ajax({
url: url,
type: 'post',
dataType: 'text',
cache: false,
async: false,
data: data,
success: function (data) {
window.top.location.href = window.top.location.href;
},
error: function (error) {
alert("An error has occured and the window will not be closed.");
}
});
}
//Single Click on BUtton
function StudentSelectClick(e) {
var windows = this.parent.$('#Window').data('tWindow');
var fromCourse = "#SessionWrapper.Student.FromCoursePage";
var fromList = "#SessionWrapper.Student.FromListingPage";
if (fromCourse == "True") {
var studentInformation = e.toString();
var data = { "myModel": "null", "studentInformation": studentInformation };
var url = '#Url.Action("UpdatedFromSelect", "StudentProcedure")';
$.ajax({
url: url,
type: 'post',
dataType: 'text',
cache: false,
async: false,
data: data,
success: function (data) {
// window.top.location.href = window.top.location.href;
windows.close();
// setTimeout(this.window.top.location.href = this.window.top.location.href, 1000);
window.top.location.href = window.top.location.href;
},
error: function (error) {
alert("An error has occured and the window will not be closed.");
}
});
}
}
This is the method where the double is being posted to. It simply redirect to a different method of return type ActionResult that also does a redirect to the index page of return ActionResult:
public string Student(string StudentID, string StudentDescription, string action)
{
if (StudentDescription != null)
{
StudentDescription = HttpUtility.HtmlDecode(StudentDescription);
}
try
{
AddStudent(StudentID, StudentDescription, action);
}
catch (Exception e)
{
return "Error " + e.ToString();
}
return "Success";
}
Your help would be greatly appreciated, thanks.
Have you checked the number of times jQuery and the Unobtrusive scripts are added in your html? I had an issue in a previous project where one of these was duplicated and caused a double post.

Categories