Overview
I developed an employment application that has several different features. Long story short, there are a few sections within the application that allow you to add employment history, add address history, accident and license history, etc. At the end of the whole shebang, I build a JSON object and prepare it for submission to the server. That's the gist.
The Problem
Let's say you have a ton of job history over the last ten years, and you obviously will put almost all of it that you can remember, or that is relevant on the application. Let's say it's 12 jobs. Let's say you also have 3 total addresses in your address history. This will give us a total of 13 clone objects. Now the loop that I generated will read these just fine, but what happens is, and I'm guessing it's my loop structure, is that when the count on the cloned objects gets so high, it starts to cause a timeout error while looping and basically freezes.
The Fix
I need to find the error in my loop that may or may not be causing this.
I'll post my code below and a link to the live application so anyone who wants to take a shot at this can play around with it and see what I mean. It's taken a couple days of some incredible debugging to even get this far with it.
Resources
$('#applicationForm').submit(function(e){
e.preventDefault(); //stop the form from the default submission
$('body').spin('large');
var application_info = new Object(); //start the application form Object
$('#submit-btn').prop('disabled', true);
if(checkHeadings(sections)){
for(var i = 0; i < sections.length; i++){
application_info[sections[i]] = new Object(); //create a new object for each section
//traverse each select by form control
$("#"+sections[i]).find(".form-control").map(function (index, element){
$(element).each(function (index){
var name = $(element).attr('name');
if((sections[i] !== 'addressRecords') && (sections[i] !== 'employmentHistory') && (sections[i] !== 'drivingExperience')){
application_info[sections[i]][$(element).attr('name')] = $('[name="'+name+'"]').eq(index).val(); //application_info
}else if(sections[i] === 'addressRecords'){
application_info['addresses'] = $('.form-control').map(function (index, element) {
return {
line1: $('[name="address"]').eq(index).val(),
line2: $('[name="address2"]').eq(index).val(),
city: $('[name="city"]').eq(index).val(),
state: $('[name="state"]').eq(index).val(),
zip: $('[name="zip"]').eq(index).val(),
from_date: $('[name="from_date"]').eq(index).val(),
to_date: $('[name="to_date"]').eq(index).val()
};
}).get();
}else if(sections[i] === 'employmentHistory'){
application_info['employmentHistory'] = $('.form-control').map(function (index, element) {
return {
from_date: $('[name="emp_from_date"]').eq(index).val(),
to_date: $('[name="emp_to_date"]').eq(index).val(),
company: $('[name="company"]').eq(index).val(),
contact: $('[name="supervisor"]').eq(index).val(),
phone: $('[name="company_phone"]').eq(index).val(),
address: $('[name="company_address"]').eq(index).val(),
city: $('[name="company_city"]').eq(index).val(),
state: $('[name="company_state"]').eq(index).val(),
zip: $('[name="company_zip"]').eq(index).val(),
position_held: $('[name="position"]').eq(index).val(),
reason_left: $('[name="reason_left"]').eq(index).val(),
fmscr: $('.fmscr:checked').eq(index).val(),
drug_testing: $('.drug_testing:checked').eq(index).val()
};
}).get();
}else if(sections[i] === 'drivingExperience'){
application_info['drivingExperience'] = {
tt_from_date : $('[name="tt-from-date"]').eq(index).val(),
tt_to_date : $('[name="tt-to-date"]').eq(index).val(),
tt_miles : $('[name="tt-miles"]').eq(index).val(),
st_from_date : $('[name="st-from-date"]').eq(index).val(),
st_to_date : $('[name="st-to-date"]').eq(index).val(),
st_miles : $('[name="st-miles"]').eq(index).val(),
accident_records : $('.form-control').map(function (index, element) {
return {
date : $('[name="accident-date"]').eq(index).val(),
nature : $('[name="nature"]').eq(index).val(),
location : $('[name="location"]').eq(index).val(),
fatalities : $('[name="fatalities"]').eq(index).val(),
injuries : $('[name="injuries"]').eq(index).val()
};
}).get(),
traffic_citations : $('.form-control').map(function (index, element) {
return {
location : $('[name="citation-location"]').eq(index).val(),
date : $('[name="citation-date"]').eq(index).val(),
charge : $('[name="charge"]').eq(index).val(),
penalty : $('[name="penalty"]').eq(index).val()
};
}).get(),
license_records : $('.form-control').map(function (index, element) {
return {
state : $('[name="license_state"]').eq(index).val(),
license_no : $('[name="license_no"]').eq(index).val(),
type : $('[name="license_type"]').eq(index).val(),
endorsements : $('[name="endorsements"]').eq(index).val(),
date : $('[name="license_date"]').eq(index).val()
};
}).get(),
qa : $('[name="qa"]:checked').eq(index).val(),
qa_explain : $('[name="qa_explain"]').eq(index).val(),
qb : $('[name="qb"]:checked').eq(index).val(),
qb_explain : $('[name="qb_explain"]').eq(index).val(),
qc : $('[name="qc"]:checked').eq(index).val(),
qc_explain : $('[name="qc_explain"]').prop('checked') ? 1 : -1,
qd : $('[name="qd"]:checked').eq(index).val()
};
}
});
}).get();
if($('input[name="other"]').is(":visible")){
application_info['generalInformation']['other'] = $('input[name="other"]').val();
}else{
application_info['generalInformation']['other'] = "";
}
application_info['selfIdentification'] = new Object();
application_info['selfIdentification']['race'] = $('[name="race"]').is(":checked") ? $('[name="race"]:checked').val() : "";
application_info['selfIdentification']['gender'] = $('[name="gender"]').is(":checked") ? $('[name="gender"]:checked').val() : "";
application_info['selfIdentification']['disability'] = $('[name="disability"]').is(":checked") ? $('[name="disability"]:checked').val() : "";
application_info['selfIdentification']['veteran'] = $('[name="veteran"]').is(":checked") ? $('[name="veteran"]:checked').val() : "";
}
$.ajax({
url: '../assets/server/application_process.php',
type : 'post',
data : application_info,
dataType : 'JSON',
success : function (data){
$('body').spin(false);
if(!data.errors){
$('#applicationForm').html("<h3>"+data.message+"</h3>");
}else{
bootbox.alert(data.message);
}
}
});
}else{
$('body').spin(false);
$('#submit-btn').prop('disabled', false);
}
});
The application:
http://www.driveforeagle.com/apply/page2
In this part it seems like you have extra iteration.
$("#"+sections[i]).find(".form-control").map(function (index, element){
$(element).each(function (index){
...
})
})
You don't need $(element).each(function (index){...} - you are already iterating the selects in map.
EDIT
I tried to refactor your code as I understood your logic, expand below. There is still room for optimization but I hope that helps.
$('#applicationForm').submit(function(e) {
e.preventDefault(); //stop the form from the default submission
$('body').spin('large');
var application_info = { //start the application form Object
generalInformation: {
other: $('input[name="other"]').is(":visible") ? $('input[name="other"]').val() : ""
},
selfIdentification: {
race: $('[name="race"]').is(":checked") ? $('[name="race"]:checked').val() : "",
gender: $('[name="gender"]').is(":checked") ? $('[name="gender"]:checked').val() : "",
disability: $('[name="disability"]').is(":checked") ? $('[name="disability"]:checked').val() : "",
veteran: $('[name="veteran"]').is(":checked") ? $('[name="veteran"]:checked').val() : ""
}
};
$('#submit-btn').prop('disabled', true);
if (checkHeadings(sections)) {
var obj = {};
for (var i = 0; i < sections.length; i++) {
var sectionId = sections[i],
$section = $("#" + sectionId);
//traverse each select by form control
switch (sectionId) {
case 'addressRecords':
obj['addresses'] = [];
$section.find('[name="address"]').each(function(index, element) {
obj['addresses'].push({
line1: $section.find('[name="address"]').eq(index).val(),
line2: $section.find('[name="address2"]').eq(index).val(),
city: $section.find('[name="city"]').eq(index).val(),
state: $section.find('[name="state"]').eq(index).val(),
zip: $section.find('[name="zip"]').eq(index).val(),
from_date: $section.find('[name="from_date"]').eq(index).val(),
to_date: $section.find('[name="to_date"]').eq(index).val()
});
});
break;
case 'employmentHistory':
obj['employmentHistory'] = [];
$section.find('[name="address"]').each(function(index, element) {
obj['employmentHistory'].push({
from_date: $section.find('[name="emp_from_date"]').eq(index).val(),
to_date: $section.find('[name="emp_to_date"]').eq(index).val(),
company: $section.find('[name="company"]').eq(index).val(),
contact: $section.find('[name="supervisor"]').eq(index).val(),
phone: $section.find('[name="company_phone"]').eq(index).val(),
address: $section.find('[name="company_address"]').eq(index).val(),
city: $section.find('[name="company_city"]').eq(index).val(),
state: $section.find('[name="company_state"]').eq(index).val(),
zip: $section.find('[name="company_zip"]').eq(index).val(),
position_held: $section.find('[name="position"]').eq(index).val(),
reason_left: $section.find('[name="reason_left"]').eq(index).val(),
fmscr: $section.find('.fmscr:checked').eq(index).val(),
drug_testing: $section.find('.drug_testing:checked').eq(index).val()
});
});
break;
case 'drivingExperience':
obj['drivingExperience'] = {
tt_from_date: $section.find('[name="tt-from-date"]').eq(0).val(),
tt_to_date: $section.find('[name="tt-to-date"]').eq(0).val(),
tt_miles: $section.find('[name="tt-miles"]').eq(0).val(),
st_from_date: $section.find('[name="st-from-date"]').eq(0).val(),
st_to_date: $section.find('[name="st-to-date"]').eq(0).val(),
st_miles: $section.find('[name="st-miles"]').eq(0).val(),
accident_records: [],
traffic_citations: [],
license_records: [],
qa: $section.find('[name="qa"]:checked').eq(0).val(),
qa_explain: $section.find('[name="qa_explain"]').eq(0).val(),
qb: $section.find('[name="qb"]:checked').eq(0).val(),
qb_explain: $section.find('[name="qb_explain"]').eq(0).val(),
qc: $section.find('[name="qc"]:checked').eq(0).val(),
qc_explain: $section.find('[name="qc_explain"]').prop('checked') ? 1 : -1,
qd: $section.find('[name="qd"]:checked').eq(0).val()
};
$section.find('[name="accident-date"]').each(function(index, element) {
obj['accident_records'].push({
date: $section.find('[name="accident-date"]').eq(index).val(),
nature: $section.find('[name="nature"]').eq(index).val(),
location: $section.find('[name="location"]').eq(index).val(),
fatalities: $section.find('[name="fatalities"]').eq(index).val(),
injuries: $section.find('[name="injuries"]').eq(index).val()
});
});
$section.find('[name="citation-location"]').each(function(index, element) {
obj['traffic_citations'].push({
location: $section.find('[name="citation-location"]').eq(index).val(),
date: $section.find('[name="citation-date"]').eq(index).val(),
charge: $section.find('[name="charge"]').eq(index).val(),
penalty: $section.find('[name="penalty"]').eq(index).val()
});
});
$section.find('[name="license_state"]').each(function(index, element) {
obj['license_records'].push({
state: $section.find('[name="license_state"]').eq(index).val(),
license_no: $section.find('[name="license_no"]').eq(index).val(),
type: $section.find('[name="license_type"]').eq(index).val(),
endorsements: $section.find('[name="endorsements"]').eq(index).val(),
date: $section.find('[name="license_date"]').eq(index).val()
});
});
break;
default:
// = if (( !== 'addressRecords') && (sections[i] !== 'employmentHistory') && (sections[i] !== 'drivingExperience')) {
obj[sectionId][element.name] = element.value;
break;
}
application_info[sectionId] = obj;
}
$.ajax({
url: '../assets/server/application_process.php',
type: 'post',
data: application_info,
dataType: 'JSON',
success: function(data) {
$('body').spin(false);
if (!data.errors) {
$('#applicationForm').html("<h3>" + data.message + "</h3>");
} else {
bootbox.alert(data.message);
}
}
});
} else {
$('body').spin(false);
$('#submit-btn').prop('disabled', false);
}
});
Related
Ext js View
There is option called treelist in the view. when use some static data in the tree list it working fine. when changed to dynamic data load from store it not loading.
Ext.define('Count.view.History', {
extend : 'Ext.Panel',
xtype : 'historyView',
controller : 'main',
requires : ['Count.store.History'],
width : '100%',
height : '100%',
title : 'History',
closable : true,
autoDestroy : true,
centered : true,
layout : 'fit',
fullscreen : true,
scrollable : true,
items :
[
{
xtype : 'tree',
store : 'History'
}
],
});
Store
Ext.define('Count.store.History', {
extend : 'Ext.data.TreeStore',
autoLoad : false,
alias : 'store.HistoryStore',
requires : ['Count.Db', 'Count.model.history'],
config :
{
model : 'Count.model.history'
},
loadData : function()
{
var meObj=this;
var sqlString = "SELECT tbl_list.ListName, tbl_list.MasterCount, tbl_sub_count.masterCountId, tbl_sub_count.subCount FROM tbl_list INNER JOIN tbl_sub_count ON tbl_list.Id=tbl_sub_count.masterCountID where tbl_sub_count.status='1';";
Count.Db.selectQuery(sqlString, meObj.callbackLoadData, meObj);
},
callbackLoadData : function(results, scope)
{
var store = scope;
var len = results.rows.length;
var MainListArray = {'root': {'expanded': true, 'children': []}};
var masterCountId = "";
var resultObj = "";
for (var i=0; i<len; i++)
{console.log(results);
if(masterCountId == "" || masterCountId != results.rows.item(i).masterCountId)
{
if(resultObj != "")
{
MainListArray.root.children.push(resultObj);
}
resultObj = {'ListName': results.rows.item(i).ListName, 'expanded': true, 'children': []}
masterCountId = results.rows.item(i).masterCountId;
var subListObj = {'subCount': results.rows.item(i).subCount, 'leaf': true}
resultObj.children.push(subListObj);
}
else
{
var subListObj = {'subCount': results.rows.item(i).subCount, 'leaf': true}
resultObj.children.push(subListObj);
}
}
if(resultObj != "")
{
MainListArray.root.children.push(resultObj);
}
console.log(MainListArray);
store.setData(MainListArray);
}
});
Controller
onShowHistory:function()
{
var showHistoryView = Ext.create('Count.view.History');
var storeHistory = Ext.create('Count.store.History');
storeHistory.loadData();
Ext.Viewport.add(showHistoryView);
}
But when call loadData function in store data looping loading infinitely?
I tried all the solution before answered few solutions. But it won't work.
Anyone please suggest me good solution for this.
Thanks in Advance.
I can't test without a working fiddle, anyway it seems you are not filling the TreeStore correctly.
Here is some changes can help you to load/view data correctly. First try to initialize the store with an empty root node adding root: {} in your store config.
And then try to load data using setRoot instead of setData, change
var MainListArray = {'root': {'expanded': true, 'children': []}};
with
var MainListArray = {'ListName': 'root', 'expanded': true, 'children': []};
and then
store.setData(MainListArray);
with
store.setRoot(MainListArray);
Finally got the solution.
Store.js
Ext.define('Count.store.History', {
extend : 'Ext.data.TreeStore',
alias : 'store.HistoryStore',
requires : ['Count.Db', 'Count.model.History'],
autoLoad: false,
rootProperty: "root",
root: {},
loadData : function()
{
var meObj=this;
var sqlString = "SELECT list.ListName, list.Count, sub_count.masterCountId, tbl_sub_count.subCount FROM tbl_japa_list INNER JOIN tbl_sub_count ON list.Id=tbl_sub_count.masterCountID where tbl_sub_count.status='1';";
Count.Db.selectQuery(sqlString, meObj.callbackLoadData, meObj);
}, callbackLoadData : function(results, scope)
{
var store = scope;
var len = results.rows.length;
var MainListArray = { 'expanded': true, 'children': []};
var CountId = "";
var resultObj = "";
for (var i=0; i<len; i++)
{
if(CountId == "" || CountId != results.rows.item(i).CountId)
{
if(resultObj != "")
{
MainListArray.children.push(resultObj);
}
resultObj = {'text': results.rows.item(i).ListName, 'expanded': true, 'children': []}
CountId = results.rows.item(i).CountId;
var subListObj = {'text': results.rows.item(i).subCount, 'leaf': true}
resultObj.children.push(subListObj);
}
else
{
var subListObj = {'text': results.rows.item(i).sub, 'leaf': true}
resultObj.children.push(subListObj);
}
}
if(resultObj != "")
{
MainListArray.children.push(resultObj);
}
store.setRoot(MainListArray);
}
View .js
item :[ { xtype : 'treelist' }]
I am using select2 group option with infinite scroll and data are coming by Ajax calling per page 10. Here is some problem arises, suppose user 1 has 15 data and user 2 has 15 data, at first 10 data are coming from user 1 and in next page 10 (5 data for user1 and 5 data for user2). no problem for data getting but the problem is user 1 group showing double. How can I prevent double display to my select2 options group? Has there any way to make an option group again?
HTML CODE
<div class="container">
<form id="frm">
<h1>Solution 1</h1>
<div class="row">
<div class="col-4">
<div class="form-group">
<label for="tagInput">Get data by ajax calling</label>
<select class="form-control" id="pictures_tag_input">
</select>
<small class="form-text text-muted"><p class="text-info">Infinite Scroll</p></small>
</div>
</div>
</div>
</form>
</div>
JS CODE
$(document).ready(function() {
// solution 1
//example.com/articles?page[number]=3&page[size]=1
http: $("#pictures_tag_input").select2({
placeholder: "Search for options",
ajax: {
url: "https://jsonplaceholder.typicode.com/users/1/todos",
dataType: "json",
global: false,
cache: true,
delay: 250,
minimumInputLength: 2,
data: function(params) {
// console.log(params.page || 1);
return {
q: params.term, // search term
_page: params.page || 1,
_limit: 10 // page size
};
},
processResults: function(data, params) {
params.page = params.page || 1;
var datx = getNestedChildren(data);
// console.log(datx);
return {
results: datx,
pagination: {
more: true
}
};
} //end of process results
} // end of ajax
});
function getNestedChildren(list) {
var roots = [];
for (i = 0; i < list.length; i += 1) {
node = list[i];
if (roots.length === 0) {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
roots.push(obj);
} else {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
var rootArray = $.map(roots, function(val, i) {
var vl = "User " + node.userId;
if (val.text === vl) return val;
else return undefined;
});
if (rootArray.length > 0) {
var obj1 = {
id: node.id,
text: node.title
};
rootArray[0].children.push(obj1);
} else {
roots.push(obj);
}
}
}
return roots;
}
});
Demo
https://codepen.io/mdshohelrana/pen/MLVZEG
Just try to use the following code
templateResult: function(data) {
if (typeof data.children != 'undefined') {
$(".select2-results__group").each(function() {
if (data.text == $(this).text()) {
return data.text = '';
}
});
}
return data.text;
}
NOTE: Need to group from server side, Other wise you have to make master details from client side.
the accepted answer didn't work for me and I don't see why should that work. A return in the $.each will not return from the templateResult() function.
Here is an approach that worked for me.
It is not necessary to build a nested list by getNestedChildren(list) on javascript side. It is much easier to build it on server side instead.
The appearance of search results in the dropdown (options and the optgroups) can be customized by using the templateResult option. I removed the duplicated optgroups and labels by this option.
check the templateResult: formatOptions, part of the code
$(document).ready(function() {
$("#pictures_tag_input").select2({
placeholder: "Search for options",
templateResult: formatOptions,
ajax: {
url: "https://jsonplaceholder.typicode.com/users/1/todos",
dataType: "json",
global: false,
cache: true,
delay: 250,
minimumInputLength: 2,
data: function(params) {
return {
q: params.term,
_page: params.page || 1,
_limit: 10
};
},
processResults: function(data, params) {
params.page = params.page || 1;
return {
results: data,
pagination: {
more: true
}
};
} //end of process results
} // end of ajax
});
function formatOptions(item, container, $el) {
// optgroups section
if (item.children && item.children.length > 0) {
// don't format the repeated optgroups!
if ($(".select2-results__group").text() === item.text) {
return;
}
if ($('[aria-label="' + item.text + '"]').length > 0) {
return;
}
// the first occasion of the given optgroup
return $el;
}
// options section
// here you can implement your own logic
// if you want to customise the output of the options
$el.addClass('something-special-result result');
return $el;
}
});
maybe the problem is a source of a data
You call user 1 .... server return a 1
You call user 2 .... server return a 1
You call user 3 .... server return a 2
You call user 4 .... server return a 2
You call user 5 .... server return a 3
You call user 6 .... server return a 3
curent_user = 1;
$(document).ready(function() {
http: $("#pictures_tag_input").select2({
placeholder: "Search for options",
ajax: {
url: "https://jsonplaceholder.typicode.com/users/1/todos",
dataType: "json",
global: false,
cache: false,
minimumInputLength: 2,
data: function(params) {
console.log("params",params || 1);
return {
q: params.term, // search term
_page: curent_user,
_limit: 10 // page size
};
},
processResults: function(data, params) {
curent_user += 2;
var datx = getNestedChildren(data);
console.log("data: ", data);
return {
results: datx,
pagination: {
more: true
}
};
} //end of process results
} // end of ajax
});
function getNestedChildren(list) {
var roots = [];
for (i = 0; i < list.length; i += 1) {
node = list[i];
if (roots.length === 0) {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
roots.push(obj);
} else {
var obj = {
text: "User " + node.userId,
children: [{ id: node.id, text: node.title }]
};
var rootArray = $.map(roots, function(val, i) {
var vl = "User " + node.userId;
if (val.text === vl) return val;
else return undefined;
});
if (rootArray.length > 0) {
var obj1 = {
id: node.id,
text: node.title
};
rootArray[0].children.push(obj1);
} else {
roots.push(obj);
}
}
}
return roots;
}
});
so if you skip a one step
You call user 1 .... server return a 1
You call user 3 .... server return a 2
You call user 5 .... server return a 3
I just found a better solution which does not result in a (duplicated) optgroup being rendered as an empty option:
processResults: function( json, params ){
setTimeout( function() {
var $prevOptions = false;
var $prevGroup = false;
// loop
$('.select2-results__option[role="group"]').each(function(){
// vars
var $options = $(this).children('ul');
var $group = $(this).children('strong');
// compare to previous
if( $prevGroup && $prevGroup.text() === $group.text() ) {
$prevOptions.append( $options.children() );
$(this).remove();
return;
}
// update vars
$prevOptions = $options;
$prevGroup = $group;
});
}, 1 );
return json;
}
Advanced Custom Fields uses the exact same code for their WordPress plugin in order to fix this issue, ajax-load and group posts from different post-types.
I'm having an issue with Kendo Grid in Angular where the custom drop down I've implemented will not open when tab navigating to that column. The built in text and number editor fields are editable on tab navigation but my custom drop down will not expand. I have to click on it to get the drop down effect.
My goal here is to allow the user to log an an entire row of data without having to take their hands off the keyboard.
My column is defined like so:
gridColumns.push({
field: currentField.FieldName.replace(/ /g, "_"),
title: currentField.FieldName,
editor: $scope.dropDownAttEditor,
template: function (dataItem) {
return $scope.dropDownTemplate(dataItem, currentField.FieldName);
}
});
My gridOptions are defined as follows:
$scope.gridOptions = {
dataSource: new kendo.data.DataSource({
transport: {
read: {
url: appconfig.basePath + '/api/DrillHoleManager/DrillHoleInterval',
type: 'POST',
contentType: 'application/json'
},
update: {
url: appconfig.basePath + '/api/DrillHoleManager/DrillHoleIntervalUpdate',
type: 'POST',
contentType: 'application/json'
},
parameterMap: function (data, operation) {
if (operation === "read") {
data.DrillHoleId = $scope.entity.Id;
data.DrillHoleIntervalTypeId = $stateParams.ddhinttypeid;
// convert the parameters to a json object
return kendo.stringify(data);
}
if (operation === 'update') {
// build DrillHoleIntervalDto object with all ATT/CMT values to send back to server
var drillHoleInterval = { Id: data.Id, Name: data.Name, From: data.From, To: data.To };
drillHoleInterval.Attributes = [];
drillHoleInterval.Comments = [];
var attributeFields = $.grep($scope.currentFields, function (e) { return e.DrillHoleTabFieldType == DrillHoleTabFieldTypeEnum.IntervalAttribute });
$.each(attributeFields, function (idx, attributeField) {
drillHoleInterval.Attributes.push({
Id: attributeField.AttributeDto.Id,
LookupId: data[attributeField.FieldName.replace(/ /g, "_")]
});
});
var commentFields = $.grep($scope.currentFields, function (e) { return e.DrillHoleTabFieldType == DrillHoleTabFieldTypeEnum.IntervalComment });
$.each(commentFields, function (idx, commentField) {
drillHoleInterval.Comments.push({
Id: commentField.CommentDto.Id,
Value: ((data[commentField.FieldName.replace(/ /g, "_")] != "") ? data[commentField.FieldName.replace(/ /g, "_")] : null)
});
});
return kendo.stringify(drillHoleInterval);
}
// ALWAYS return options
return options;
}
},
schema: { model: { id : "Id" }},
serverPaging: false,
serverSorting: false,
serverFiltering: false
//,autoSync: true
}),
columns: gridColumns,
dataBound: onDataBound,
autoBind: false,
navigatable: true,
scrollable: false,
editable: true,
selectable: true,
edit: function (e) {
var grid = $("#ddhintgrid").data("kendoGrid");
//grid.clearSelection();
grid.select().removeClass('k-state-selected');
// select the row currently being edited
$('[data-uid=' + e.model.uid + ']').addClass('k-state-selected');
e.container[0].focus();
}
};
Here is a custom event to handle the 'Tab' keypress. The point of this is I want a new record automatically added to the grid if the user presses 'Tab' at the end of the last line:
$("#ddhintgrid").keydown(function (e) {
if (e.key == "Tab") {
var grid = $("#ddhintgrid").data("kendoGrid");
var data = grid.dataSource.data();
var selectedItem = grid.dataItem(grid.select());
var selectedIndex = null
if (selectedItem != null) {
selectedIndex = grid.select()[0].sectionRowIndex;
if (selectedIndex == data.length - 1) { // if the bottom record is selected
// We need to manually add a new record here so that the new row will automatically gain focus.
// Using $scope.addRecord() here will add the new row but cause the grid to lose focus.
var newRecord = { From: grid.dataSource.data()[selectedIndex].To };
var currentCmtFields = $.grep($scope.currentFields, function (e) { return e.DrillHoleTabFieldType == DrillHoleTabFieldTypeEnum.IntervalComment; });
$.each(currentCmtFields, function (idx, currentCmtField) {
newRecord[currentCmtField.FieldName.replace(/ /g, "_")] = null;
});
grid.dataSource.insert(data.length, newRecord);
// edit the new row
grid.editRow($("#ddhintgrid tr:eq(" + (data.length) + ")"));
}
}
}
});
Here is my template for the drop down column:
$scope.dropDownTemplate = function (dataItem, fieldName) {
var currentLookups = $.grep($scope.currentFields, function (e) { return e.FieldName == fieldName; })[0].AttributeDto.Lookups;
var selectedLookup = $.grep(currentLookups, function (e) { return e.Id == dataItem[fieldName.replace(/ /g, "_")]; })[0];
// With the custom dropdown editor when going from null to a value the entire lookup object (id, name) is placed in the
// dataItem[field_name] instead of just the id. We need to replace this object with just the id value and return the name of
// the lookup to the template.
if (typeof selectedLookup == 'undefined' && dataItem[fieldName.replace(/ /g, "_")] != null) {
selectedLookup = angular.copy(dataItem[fieldName.replace(/ /g, "_")]);
dataItem[fieldName.replace(/ /g, "_")] = selectedLookup.Id;
}
if (selectedLookup != null && selectedLookup != '') {
return selectedLookup.Name;
}
else {
return '';
}
};
And finally here is the custom editor for the drop down column:
$scope.dropDownAttEditor = function (container, options) {
var editor = $('<input k-data-text-field="\'Name\'" k-data-value-field="\'Id\'" k-data-source="ddlDataSource" data-bind="value:' + options.field + '"/>')
.appendTo(container).kendoDropDownList({
dataSource: $.grep($scope.currentFields, function (e) { return e.FieldName == options.field.replace(/_/g, ' '); })[0].AttributeDto.Lookups,
dataTextField: "Name",
dataValueField: "Id"
});
};
I know this is a lot to take in but if you have any questions just let me know.
My ultimate goal is that I want the user to be able to navigate the grid using the 'Tab' key and edit every field without having to use the mouse.
I need a filter with an "Apply" button and without autocomplete. I can't do that. Here's my basic code, with autocomplete (I don't want it) and without button (I want this).
I'm using Jade.
search.jade
input(type="text", ng-model="buscador.id", placeholder="Ejemplo: 111")
listmagazines.jade
.col-xlg-25.col-sm-3.col-xs-3.col-xxs-6(ng-repeat="magazine in magazines|filter:buscador")
app.js It has a lot of items, I'll put just three.
var app = angular.module("appBuscador", []);
app.factory("revistas", function(){
return{
"revistasNumeros":[
{
id: 97,
titulo: "Nº97",
img: "pdf97.jpg",
descarga: "",
url: "http://www.mapfre.com/fundacion/html/revistas/gerencia/n097/index.html"
},
{
id: 98,
titulo: "Nº98",
img: "pdf98.jpg",
descarga: "",
url: "http://www.mapfre.com/fundacion/html/revistas/gerencia/n098/index.html"
},
{
id: 99,
titulo: "Nº99",
img: "pdfdefault.jpg",
descarga: "",
url: "http://www.mapfre.com/fundacion/html/revistas/gerencia/n099/index.html"
}
]
}
})
controller.js
app.controller("revistasController", function($scope,revistas){
$scope.magazines = revistas.revistasNumeros;
})
Thank you.
--------------------- SORRY I CAN'T ADD AN ANSWER I'M BANNED
I found the solution:
search.jade (add a button)
input(type="text", ng-model="buscador.id", placeholder="Ejemplo: 111")
a.btn.btn-buscador(href="#", ng-click="applySearchFilter()") Buscar
listmagazines.jade (quit filter: and add the ng-hide)
.col-xlg-25.col-sm-3.col-xs-3.col-xxs-6(ng-repeat="magazine in magazines", ng-hide="magazine.excludedByFilter")
controller.js (add this to your app.controller and change the properties)
app.controller("revistasController", function($scope,revistas){
$scope.magazines = revistas.revistasNumeros;
$scope.applySearchFilter = function() {
var idFilter = $scope.buscador.id;
console.log(idFilter);
var showAll = 0 === idFilter.length;
angular.forEach($scope.magazines, function(magazine) {
var id = magazine.id.toString();
if (showAll) {
magazine.excludedByFilter = false;
} else {
magazine.excludedByFilter = (id.indexOf(idFilter) === -1)
|| (id.indexOf(idFilter) === -1);
}
});
}
})
It works!! :)
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 10 years ago.
Improve this question
I am trying to write an app that will work online and offline using technologies such as application cache and local storage. I am using jQuery mobile and an jqm autocomplete solution which can be found here http://andymatthews.net/code/autocomplete/
The idea is that if the app is on-line then I will be calling a remotely accessible function via ajax which will set data. The ajax calls that are made bring back data for events, countries and hospitals. Once the data as been returned I am setting the data in localStorage.
If the data already exists in localStorage then I can continue while being offline as I don't have to rely on the ajax calls.
I am having performance issues with this code when running on iPad/mobile devices. Where the hospital data is concerned. There is a large amount of data being returned for hospitals, please see sample of JSON for hospital data below. It can be a little slow on desktop but not nearly as bad.
Can anyone see any improvements to the code I have below to increase the performance.
JSON hospital data example
{ "hospitals" : {
"1" : { "country" : "AD",
"label" : "CAIXA ANDORRANA SEGURETAT SOCIAL"
},
"10" : { "country" : "AE",
"label" : "Advance Intl Pharmaceuticals"
},
"100" : { "country" : "AT",
"label" : "BIO-KLIMA"
},
"1000" : { "country" : "BE",
"label" : "Seulin Nicolas SPRL"
},
"10000" : { "country" : "ES",
"label" : "Dental 3"
},
"10001" : { "country" : "ES",
"label" : "PROTESIS DENTAL MACHUCA PULIDO"
},
"10002" : { "country" : "ES",
"label" : "JUST IMPLANTS, S.L."
},
"10003" : { "country" : "ES",
"label" : "CTRO DENTAL AGRIC ONUBENSE DR.DAMIA"
},
"10004" : { "country" : "ES",
"label" : "HTAL. VIRGEN DE ALTAGRACIA"
},
"10005" : { "country" : "ES",
"label" : "HOSPITAL INFANTA CRISTINA"
}....
/*global document,localStorage,alert,navigator: false, console: false, $: false */
$(document).ready(function () {
//ECMAScript 5 - It catches some common coding bloopers, throwing exceptions. http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
//It prevents, or throws errors, when relatively "unsafe" actions are taken (such as gaining access to the global object).
//It prevents, or throws errors, when relatively "unsafe" actions are taken (such as gaining access to the global object).
"use strict";
//initialise online/offline workflow variables
var continueWorkingOnline, continueWorkingOffline, availableEvents, availableHospitals, availableCountries, availableCities;
continueWorkingOnline = navigator.onLine;
var getLocalItems = function () {
var localItems = [];
availableEvents = localStorage.getItem('eventData');
availableHospitals = localStorage.getItem('hospitalData');
availableCountries = localStorage.getItem('countryData');
if (availableEvents) {
//only create the array if availableEvents exists
localItems[0] = [];
localItems[0].push(availableEvents);
}
if (availableCountries) {
//only create the array if availableCountries exists
localItems[1] = [];
localItems[1].push(availableCountries);
}
if (availableHospitals) {
//only create the array if availableHospitals exists
localItems[2] = [];
localItems[2].push(availableHospitals);
}
if (availableCities) {
//only create the array if availableHospitals exists
localItems[3] = [];
localItems[3].push(availableCities);
}
return localItems;
};
//Check to see if there are 3 local items. Events, Countries, Cities. If true we know we can still run page off line
continueWorkingOffline = getLocalItems().length === 3 ? true: false;
//Does what is says on the tin
var populateEventsDropDown = function (data) {
var eventsDropDown = $('#eventSelection');
var item = data.events;
$.each(item, function (i) {
eventsDropDown.append($('<option></option>').val(item[i].id).html(item[i].name));
});
};
//Called once getData's success call back is fired
var setFormData = function setData(data, storageName) {
//localStorage.setItem(storageName, data);
localStorage.setItem(storageName, data);
};
//This function is only called if continueWorkingOnline === true
var getRemoteFormData = function getData(ajaxURL, storageName) {
$.ajax({
url: ajaxURL,
type: "POST",
data: '',
success: function (data) {
setFormData(data, storageName);
}
});
};
//Function for autoComplete on Hospital data
var autoCompleteDataHospitals = function (sourceData) {
var domID = '#hospitalSearchField';
var country = $('#hiddenCountryID').val();
var items = $.map(sourceData, function (obj) {
if (obj.country === country) {
return obj;
}
});
$(domID).autocomplete({
target: $('#hospitalSuggestions'),
source: items,
callback: function (e) {
var $a = $(e.currentTarget);
$(domID).val($a.data('autocomplete').label);
$(domID).autocomplete('clear');
}
});
};
//Function for autoComplete on Country data
var autoCompleteDataCountries = function (sourceData) {
var domID = '#countrySearchField';
var domHiddenID = '#hiddenCountryID';
var items = $.map(sourceData, function (obj) {
return obj;
});
$(domID).autocomplete({
target: $('#countrySuggestions'),
source: items,
callback: function (e) {
var $a = $(e.currentTarget);
$(domID).val($a.data('autocomplete').label);
$(domID).autocomplete('clear');
$(domHiddenID).val($a.data('autocomplete').value);
//enable field to enter Hospital
$('#hospitalSearchField').textinput('enable');
//Call to autoComplete function for Hospitals
autoCompleteDataHospitals(availableHospitals.hospitals);
}
});
};
if (continueWorkingOnline === false && continueWorkingOffline === false) {
alert("For best results this form should be initiated online. You can still use this but auto complete features will be disabled");
}
if (continueWorkingOnline === true && continueWorkingOffline === false) {
getRemoteFormData('templates/cfc/Events.cfc?method=getEventsArray', 'eventData');
getRemoteFormData('templates/cfc/Countries.cfc?method=getCountriesArray', 'countryData');
getRemoteFormData('templates/cfc/Hospitals.cfc?method=getHospitalsArray', 'hospitalData');
$(document).ajaxStop(function () {
//set the variables once localStorage has been set
availableEvents = JSON.parse(localStorage.getItem("eventData"));
availableHospitals = JSON.parse(localStorage.getItem('hospitalData'));
availableCountries = JSON.parse(localStorage.getItem('countryData'));
//Inserts data into the events drop down
populateEventsDropDown(availableEvents);
autoCompleteDataCountries(availableCountries.countries);
});
}
if (continueWorkingOnline === true && continueWorkingOffline === true) {
//get the localStorage which we know exists because of continueWorkingOffline is true
availableEvents = JSON.parse(localStorage.getItem('eventData'));
availableHospitals = JSON.parse(localStorage.getItem('hospitalData'));
availableCountries = JSON.parse(localStorage.getItem('countryData'));
//Inserts data into the events drop down
populateEventsDropDown(availableEvents);
autoCompleteDataCountries(availableCountries.countries);
}
});
If your bottleneck is downloading that massive json file, the only way to make it less of a bottleneck is to make it smaller or to send less data. For example, you could sort your hospitals by country and store arrays rather than objects with keys to make the json smaller rather than repeating keys over and over.
{
"AD":[
"CAIXA ANDORRANA SEGURETAT SOCIAL"
],
"AE":[
"Advance Intl Pharmaceuticals"
],
...
"ES":[
"Dental 3",
"Dental 4",
"Dental 5"
]
}
if the id field is important, use
...
"ES":[
["10000","Dental 3"],
["10001","Dental 4"],
["10002","Dental 5"]
]
You would of course need to update the rest of your code to use this json format rather than your previous.