jQuery UI autoselect question - javascript

For example, if I mistype here and enter 3 instead of 4, how can I resurrect the autocomplete function, so I get offered the options for the id's beginning with 4 after deleting the wrong id value and entering 4?
(As it is now, after deleting the 3..... id and entering 4 I get no more selection.)
#!/usr/bin/env perl
use warnings;
use 5.014;
use utf8;
use Mojolicious::Lite;
use DBI;
my $table = 'my_test_table';
my $dbh = DBI->connect( 'dbi:SQLite:dbname=my_test_db.db', '', '',
{ RaiseError => 1, PrintError => 0, AutoCommit => 1, sqlite_unicode => 1, }
) or die $DBI::errstr;
$dbh->do( "CREATE TEMP TABLE $table ( firstname TEXT, lastname TEXT, id INTEGER UNIQUE )" );
my $sth = $dbh->prepare( "INSERT INTO $table ( firstname, lastname, id ) VALUES ( ?, ?, ?)" );
$sth->execute( 'Charlie', 'Harper', '321456' );
$sth->execute( 'Rachel Karen', 'Green', '253422' );
$sth->execute( 'John', 'Dorian', '433542' );
$sth->execute( 'Homer', 'Simson', '433541' );
get '/eingabe';
get '/search_db/:opt' => sub {
my $self = shift;
my $opt = $self->param( 'opt' );
my $term = $self->param( 'term' );
my $ref;
if ( $opt eq 'autocomplete' ) {
my $sth = $dbh->prepare( "SELECT id FROM $table WHERE id LIKE ?" );
$sth->execute( $term . '%');
while ( my $row = $sth->fetchrow_arrayref() ) {
push #$ref, #$row;
}
} elsif ( $opt eq 'row' ) {
$ref = $dbh->selectall_arrayref( "SELECT * FROM $table WHERE id == ?", { Slice => {} }, $term );
die scalar #$ref if #$ref != 1;
$ref = $ref->[0];
}
$self->render( json => $ref );
};
app->start;
__DATA__
## eingabe.html.ep
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
<script type="text/javascript">
$( document ).ready( function() {
var searchID = '#id';
$( searchID ).autocomplete({
source: function( request, response ) {
$.getJSON( '/search_db/autocomplete', request, function( dataFromServer ) {
var suggestions = [];
var len = dataFromServer.length;
for ( var i = 0; i < len; i++ ) {
suggestions.push( dataFromServer[i].toString() );
}
if ( len == 1 ) {
$( searchID ).autocomplete( 'disable' );
$.getJSON( '/search_db/row', { term : suggestions[0] }, function( data ) {
for ( var key in data ) {
if( data.hasOwnProperty( key ) ) {
var input = document.getElementById( key );
input.value = data[key];
}
}
});
}
response( suggestions );
});
},
select: function( event, ui ) {
$.getJSON( '/search_db/row', { term : ui.item.value }, function( data ) {
for ( var key in data ) {
if( data.hasOwnProperty( key ) ) {
var input = document.getElementById( key );
input.value = data[key];
}
}
});
},
delay: 100
});
});
</script>
</head>
<body>
<form>
<table>
<tr><td>Firstname:</td><td><input type="text" id="firstname" name="firstname" /></td></tr>
<tr><td>Lastname:</td><td><input type="text" id="lastname" name="lastname" /></td></tr>
<tr><td>ID:</td><td><input type="number" id="id" name="id" autofocus="on" /></td></tr>
</table><br />
<input type="submit" value="OK"/>
</form>
</body>
</html>

When I add $( searchID ).autocomplete( 'enable' ); after response( suggestions );
it works better.
<script type="text/javascript">
$( document ).ready( function() {
var searchID = '#id';
$( searchID ).autocomplete({
source: function( request, response ) {
$.getJSON( '/search_db/autocomplete', request, function( dataFromServer ) {
var suggestions = [];
var len = dataFromServer.length;
for ( var i = 0; i < len; i++ ) {
suggestions.push( dataFromServer[i].toString() );
}
if ( len == 1 ) {
$( searchID ).autocomplete( 'disable' );
$.getJSON( '/search_db/row', { term : suggestions[0] }, function( data ) {
for ( var key in data ) {
if( data.hasOwnProperty( key ) ) {
var input = document.getElementById( key );
input.value = data[key];
}
}
});
}
response( suggestions );
$( searchID ).autocomplete( 'enable' );
});
},
select: function( event, ui ) {
$.getJSON( '/search_db/row', { term : ui.item.value }, function( data ) {
for ( var key in data ) {
if( data.hasOwnProperty( key ) ) {
var input = document.getElementById( key );
input.value = data[key];
}
}
});
},
delay: 100
});
});
</script>

Related

JQuery - How to check all value from array before goes to else statement?

How can I check all value from array before going to else statement.
My code only working for the first array, in my case which is /members/, but it wont work for rest of the array.
if ( jQuery( location ).attr( 'href' ) !== 'https://example.com/' ) {
let getPostType = `<?php echo $post_type = get_post_type($id); ?>`;
if ( getPostType === 'page' ) {
let URLsToAvoid = [ '/members/', '/documents/', '/photos/', '/videos/', '/register/', '/agencies/', '/news/', '/events/' ];
jQuery.each( URLsToAvoid, function( key, value ) {
if ( jQuery( location ).attr( 'href' ).includes(value) ) {
return false;
} else {
let displayHeader = `<?php echo do_shortcode( '[elementor-template id="754"]' ); ?>`;
jQuery( '.container .site-content-grid > div, .container .bb-elementor-content > div' ).prepend(displayHeader);
return false;
}
});
}
}

columnfilterwidget in Datatables Filter sort issue

I am using DataTables 1.10 with Columnfilterwidget,
Problem i am facing is
I have values like
0,1.44,10,100,100.00,20,20.13
Right now Columnfilterwidget is sorting these values alphabetically
0
1.44
10
100
100.00
20
20.13
But i want them to be filtered numerically
0
1.44
10
20
20.13
100
100.00
Like this,
This is what i have tried so far
In Columnfilterwidget.js file
ColumnFilterWidget.prototype.fnDraw = function() {
var widget = this;
var oDistinctOptions = {};
var aDistinctOptions = [];
var aData;
if ( widget.asFilters.length === 0 ) {
// Find distinct column values
aData = widget.oDataTable.fnGetColumnData( widget.iColumn );
$.each( aData, function( i, sValue ) {
var asValues = widget.sSeparator ? sValue.split( new RegExp( widget.sSeparator ) ) : [ sValue ];
$.each( asValues, function( j, sOption ) {
if ( !oDistinctOptions.hasOwnProperty( sOption ) ) {
oDistinctOptions[sOption] = true;
aDistinctOptions.push( sOption );
}
} );
} );
// Build the menu
widget.$Select.empty().append( $( '<option></option>' ).attr( 'value', '' ).text( widget.oColumn.sTitle ) );
// aDistinctOptions.sort();
aDistinctOptions.sort(function(a,b){return a - b});
$.each( aDistinctOptions, function( i, sOption ) {
var sText;
if(sOption==='')
sText = $( '<div> (Blank )</div>' ).text();
else
sText = $( '<div>' + sOption + '</div>' ).text();
widget.$Select.append( $( '<option></option>' ).attr( 'value', sOption ).text( sText ) );
} );
if ( aDistinctOptions.length > 1 ) {
// Enable the menu
widget.$Select.attr( 'disabled', false );
} else {
// One option is not a useful menu, disable it
widget.$Select.attr( 'disabled', true );
}
}
};
I changed sort function to numeric, but how can i identify the coming value is numeric, also i checked sValue values are string, so is there any way i can define Columns i want to numeric.
Also tried setting options oColumnFilterWidgets still it didn't worked.
oColumnFilterWidgets: {
"aiExclude": [ 0, 1, 10 ],
"aoColumnDefs": [
// { "bSort": false, "sSeparator": " / ", "aiTargets": [ 5 ] },
{ "fnSort": function( a, b ) { return a-b; }, "aiTargets": [6] },
{"sType": "numeric", "aiTargets":[6]}
]
},
What am i doing wrong here?
Thanks to cyberhobo who fixed this issue.
[https://github.com/cyberhobo/ColumnFilterWidgets/commit/01efef439c557154bad3d61aa00d47285b6d21d6#diff-72250e81389e20ff2fa22ff884fcb6aaR309][1]
Just made the changes he highlighted inside plugin and
"oColumnFilterWidgets": {
"aoColumnDefs": [
{ "bSort": false, "sSeparator": " / ", "aiTargets": [ 2 ] },
{ "fnSort": function( a, b ) { return a-b; }, "aiTargets": [ 3 ] }
]
}
Hope this helps.

jQuery Datatables and ColumnFilterWidget integration issue

I am trying to Place the ColumnFilterWidget plugin in the Header of the Datatables Table.
Here are the changes i made in it :
/**
* Menu-based filter widgets based on distinct column values for a table.
*
* #class ColumnFilterWidgets
* #constructor
* #param {object} oDataTableSettings Settings for the target table.
*/
var ColumnFilterWidgets = function( oDataTableSettings ) {
var me = this;
var sExcludeList = '';
// me.$WidgetContainer = $( '<div class="column-filter-widgets"></div>' );
me.$WidgetContainer = $( '<tr class="head"></tr>' );
me.$MenuContainer = me.$WidgetContainer;
me.$TermContainer = null;
me.aoWidgets = [];
me.sSeparator = '';
if ( 'oColumnFilterWidgets' in oDataTableSettings.oInit ) {
if ( 'aiExclude' in oDataTableSettings.oInit.oColumnFilterWidgets ) {
sExcludeList = '|' + oDataTableSettings.oInit.oColumnFilterWidgets.aiExclude.join( '|' ) + '|';
}
if ( 'bGroupTerms' in oDataTableSettings.oInit.oColumnFilterWidgets && oDataTableSettings.oInit.oColumnFilterWidgets.bGroupTerms ) {
me.$MenuContainer = $( '<div class="column-filter-widget-menus"></div>' );
me.$TermContainer = $( '<div class="column-filter-widget-selected-terms"></div>' ).hide();
}
}
// Add a widget for each visible and filtered column
$.each( oDataTableSettings.aoColumns, function ( i, oColumn ) {
var $columnTh = $( oColumn.nTh );
var $WidgetElem = $( '<th><div class="column-filter-widget"></div></th>' );
if ( oColumn.bVisible && sExcludeList.indexOf( '|' + i + '|' ) < 0 ) {
me.aoWidgets.push( new ColumnFilterWidget( $WidgetElem, oDataTableSettings, i, me ) );
}
me.$MenuContainer.append( $WidgetElem );
} );
if ( me.$TermContainer ) {
me.$WidgetContainer.append( me.$MenuContainer );
me.$WidgetContainer.append( me.$TermContainer );
}
oDataTableSettings.aoDrawCallback.push( {
name: 'ColumnFilterWidgets',
fn: function() {
$.each( me.aoWidgets, function( i, oWidget ) {
oWidget.fnDraw();
} );
}
} );
return me;
};
I added a extra <tr class='head'> inside the Datatable, and later on i am trying to append the Filters to that with attached to them,But instead of that it is creating new TR tag and then appending the filters in it.
I even changed my dom of data tables to : dom: '<"clear">Cf<"clear">ltWrip',
So the table elements should be there so that it can insert filters inside the head.
FOUND THE ANSWER
Here is it if anyone else needs it .
Add a <TR id='Filter.$i'> element in the html
Use a for loop and append the counter value to the ID.
then modified the column.filterwidget plugn js
var ColumnFilterWidgets = function( oDataTableSettings ) {
var me = this;
var sExcludeList = '';
me.$WidgetContainer = $( '<div class="column-filter-widgets"></div>' );
me.$MenuContainer = me.$WidgetContainer;
me.$TermContainer = null;
me.aoWidgets = [];
me.sSeparator = '';
if ( 'oColumnFilterWidgets' in oDataTableSettings.oInit ) {
if ( 'aiExclude' in oDataTableSettings.oInit.oColumnFilterWidgets ) {
sExcludeList = '|' + oDataTableSettings.oInit.oColumnFilterWidgets.aiExclude.join( '|' ) + '|';
}
if ( 'bGroupTerms' in oDataTableSettings.oInit.oColumnFilterWidgets && oDataTableSettings.oInit.oColumnFilterWidgets.bGroupTerms ) {
me.$MenuContainer = $( '<div class="column-filter-widget-menus"></div>' );
me.$MenuContainer = $( '<div class="column-filter-widget-menus"></div>' );
me.$TermContainer = $( '<div class="column-filter-widget-selected-terms"></div>' ).hide();
}
}
var cnt= 0;
// Add a widget for each visible and filtered column
$.each( oDataTableSettings.aoColumns, function ( i, oColumn ) {
var $columnTh = $( oColumn.nTh );
cnt ++;
var $WidgetElem = $( '<div class="column-filter-widget" id=col'+cnt+'></div>' );
if ( oColumn.bVisible && sExcludeList.indexOf( '|' + i + '|' ) < 0 ) {
me.aoWidgets.push( new ColumnFilterWidget( $WidgetElem, oDataTableSettings, i, me ) );
}
var Tcol = document.getElementById('A');
console.log('---------'+i);
//me.$MenuContainer.append( $WidgetElem );
$('#Filter'+i).append( $WidgetElem );
} );
if ( me.$TermContainer ) {
me.$WidgetContainer.append( me.$MenuContainer );
me.$WidgetContainer.append( me.$TermContainer );
}
oDataTableSettings.aoDrawCallback.push( {
name: 'ColumnFilterWidgets',
fn: function() {
$.each( me.aoWidgets, function( i, oWidget ) {
oWidget.fnDraw();
} );
}
} );
return me;
};
Hope this helps.

Jquery UI Dialog Buttons Firing Multiple click Events

I am working on a WP plugin for a client that communicates with UPS, USPS and FEDEX. USPS requires multiple steps so I am using a UI Dialog Modal in which I add content dynamically via AJAX (PHP). The content is great, but it seems that when I click the continue button on the first dialog it fires all successive dialog button functions. I have a function that I call to change dialog content, and each calling function sends AJAX data and sets the options for the next dialog. I need the button to simply complete its function and allow the user to make selections on the next dialog. While I have been using JQuery, JS and PHP for quite sometime I am fairly new to jQuery UI. Please see my code below for reference. Any help on this would be greatly appreciated.
"obj" is an object returned from PHP containing shippment data and html for dialog.
Function for updating dialog content
show_dialog = function( title, html, options ) {
$( "#dialog" ).dialog({
autoOpen: false,
});
$('#dialog').html( html );
$('#dialog').dialog( 'option', 'title', title );
$('#dialog').dialog( options );
$('div.ui-dialog-buttonset button.ui-button span.ui-button-text').each(function() {
$(this).html($(this).parent().attr('text'));})
}
Function for address validation
usps_address_check = function( obj ) {
if( obj.VStatus === 'AddressMatch' ) {
var title = obj.Title;
var options = {
resizable: false,
width:800,
modal:true,
buttons: {
//when this button is clicked it fires the function in the next dialog below process shipment.
'Continue': function(event) {
if( $('#verified').attr('checked') ) {
data = {
action: 'usps_ajax',
call: 'check_rate',
post: $('#post_id').val(),
};
ajax_request( data );
} else {
$(this).dialog( 'option', 'title', "Please click the checkbox to confirm corrected address..." );
}
},
Cancel: function() {
$(this).dialog( 'close' );
}
}
}
}
show_dialog( title, obj.StatusMessage, options );
$('#dialog').dialog('open');
}
Function for confirming shipping rates and selecting service addons prior to processing shipment request with USPS
usps_confirm_rates = function( obj ) {
var title = obj.Title;
var html = obj.StatusMessage;
var options = {
resizable: true,
width:800,
height:800,
modal:true,
buttons: {
//This function is fired when the button on the first modal above is clicked.
'Process Shipment': function() {
data = {
action: 'usps_ajax',
call: 'process_shipment',
post: $('#post_id').val(),
};
ajax_request( data );
},
'Cancel': function(e) {
$(this).dialog( 'close' );
}
}
}
show_dialog( title, html, options );
var total_shipping = parseFloat( $('#total_shipping').text() );
var customer_paid = parseFloat( $('#shipping_paid').text() );
var total_addons = parseFloat( $('#total_addons').text() );
var difference;
$('.btn_addons').click( function(e) {
var key = $(this).attr('id');
$('#' + key + '_addon_options').slideToggle();
$('.cb_addon_sel').change( function(e) {
var addons = obj.AddOns;
var thisCheck = $(this);
var thisTable = $(this).closest('table').attr('id');
var curr_addon = $(this).val();
var addon_name = $("#" + curr_addon + "_name").text();
var thisAddon = new Array();
var price = get_price( thisTable, curr_addon );
if( obj.AddOns[thisTable][curr_addon].ProhibitedWithAnyOf !== undefined ) {
var prohibited = obj.AddOns[thisTable][curr_addon].ProhibitedWithAnyOf.AddOnTypeV5;
}
if( obj.AddOns[thisTable][curr_addon].RequiresAllOf !== undefined ) {
var required = obj.AddOns[thisTable][curr_addon].RequiresAllOf.RequiresOneOf.AddOnTypeV5;
}
if($(this).attr('checked') ) {
total_addons += parseFloat( price );
total_shipping += parseFloat( price );
if( addons_selected[thisTable] === undefined )
addons_selected[thisTable] = new Array();
addons_selected[thisTable].push( curr_addon );
for( var p in prohibited ) {
if( typeof prohibited === 'object' )
element = prohibited[p];
else
element = prohibited;
$('#' + thisTable + '_row_' + element).hide();
if( $('#' + thisTable + '_' + element).attr('checked') ) {
$('#' + thisTable + '_' + element).removeAttr('checked');
}
}
for( var r in required ) {
if( typeof required === 'object' )
element = required[r];
else
element = required;
price = get_price( thisTable, element);
$('#' + thisTable + '_' + element).attr('checked', 'checked');
total_addons += parseFloat( price );
total_shipping += parseFloat( price );
}
} else {
var name = addon_required( curr_addon, thisTable );
if( typeof name === 'string' ) {
$('#' + curr_addon + '_info').text('Required when ' + name + ' is selected.');
$('#' + thisTable + '_' + curr_addon).attr('checked','checked');
} else {
total_addons -= parseFloat( price );
total_shipping -= parseFloat( price );
for( var p in prohibited ) {
if( typeof prohibited === 'object' )
element = prohibited[p];
else
element = prohibited;
$('#' + thisTable + '_row_' + element).show();
//removeByValue( prohibited[p], prohibited );
}
for( var r in required ) {
if( typeof required === 'object' )
element = required[r];
else
element = required;
price = get_price( thisTable, element);
$('#' + thisTable + '_' + element).attr('checked', 'checked');
$('#' + thisTable + '_' + element).removeAttr('checked');
$('#' + element + '_info').text('');
total_addons -= parseFloat( price );
total_shipping -= parseFloat( price );
//removeByValue( required[r], required );
}
removeByValue( curr_addon, addons_selected[thisTable] );
}
}
difference = customer_paid - total_shipping;
$('#total_addons').text( total_addons.toFixed(2) );
$('#total_shipping').text( total_shipping.toFixed(2) );
$('#total_difference').text( difference.toFixed(2) );
});
});
function addon_required( addon, box ) {
if( typeof required === 'undefined' ) {
return false;
} else {
for(var a in addons_selected[box]) {
var reqs = obj.AddOns[box][addons_selected[box][a]].RequiresAllOf.RequiresOneOf.AddOnTypeV5;
if( $.inArray(addon, reqs) == -1) {
return false;
} else {
return $("#" + addons_selected[a] + "_name").text();
}
}
}
}
function get_price( box, addon ) {
if( obj.AddOns[box][addon].Amount === undefined ) {
price = 0.00;
} else {
price = obj.AddOns[box][addon].Amount;
}
return price;
}
}
So I was unable to fix the issue directly, so I created a workaround per se. Instead of using the dialog buttons as navigation, I added control buttons to the dialog content in php. I then access them directly via Jquery. See example below.
public function verify_address( $authenticator ) {
$order = $this->order;
$params = array(
'Authenticator' => $authenticator,
'Address' => array(
'FullName' => $order->shipping_first_name . ' ' . $order->shipping_last_name,
'Company' => $order->shipping_company,
'Address1' => $order->shipping_address_1,
'Address2' => $order->shipping_address_2,
'City' => $order->shipping_city,
'State' => $order->shipping_state,
'Zipccode' => $order->shipping_postcode,
'Country' => $order->shipping_country
),
);
$check = $this->stamps->CleanseAddress( $params );
$this->xml_response['Call'] = 'VerifyAddress';
if( ! $check->AddressMatch ) {
if( $check->CityStateZipOK ) {
$this->xml_response['ResponseStatusCode'] = 1;
$this->xml_response['VStatus'] = 'CityStateZipOK';
$this->xml_response['StatusMessage'] = 'The street address could not be verified; however, the City, State, & ZipCode are valid. Click continue to use this address or cancel.';
}
$this->xml_response['ResponseStatusCode'] = 0;
$this->xml_response['VStatus'] = 'InvalidAddress';
$this->xml_response['StatusMessage'] = 'invalid address. Please verify address and resubmit.';
} else {
$message = '<span id="usps_error"></span></br>';
$message .= "The address was matched. Please review updated address below and click continue to proceed.";
$message .= '<table><tr><td><input type="checkbox" id="verified" value="true" /></td>';
$message .= '<td>' . $check->Address->FullName . '</br>';
$message .= $check->Address->Address1 . '</br>';
$message .= count( $check->Address->Address2 ) < 0 ? $check->Address->Address2 . '</br>' : '';
$message .= $check->Address->City . ', ' . $check->Address->State . ' ' . $check->Address->ZIPCode . '-' . $check->Address->ZIPCodeAddOn . '</td></tr><table>';
//Added html button here for navigation purposes. This can be accessed by its ID in Js after it is added to the dialog box.
$message .= '</br></br><div><button class="dialog_nav" id="btn_continue">Continue</button></div>';
$this->xml_response['ResponseStatusCode'] = 1;
$this->xml_response['VStatus'] = 'AddressMatch';
$this->xml_response['StatusMessage'] = $message;
$this->xml_response['Authenticator'] = $check->Authenticator;
$this->xml_response['Method'] = 'USPS';
$this->xml_response['Title'] = 'Step 1: Address Verfication';
}
if( is_soap_fault( $check ) ) {
$this->xml_response = handle_errors( $check );
}
return $this->xml_response;
}
Then in JS I can access the button via jQuery.
usps_address_check = function( obj ) {
if( obj.VStatus === 'AddressMatch' ) {
var title = obj.Title;
var options = {
resizable: false,
width:800,
modal:true,
buttons: {
//Cancel button functions correctly
'Cancel': function() {
$(this).dialog( 'close' );
}
}
}
show_dialog( title, obj.StatusMessage, options );
$('#dialog').dialog('open');
//Button is accessed here and is code is only executed once.
$('#btn_continue').click( function(e) {
if( $('#verified').attr('checked') ) {
data = {
action: 'usps_ajax',
call: 'check_rate',
post: $('#post_id').val(),
};
ajax_request( data );
} else {
$('#usps_error').text("Please click the checkbox to confirm corrected address, and click continue to proceed." ).css('color','red');
}
});
}
}
Still looking for a better solution, but this will work for now.

Is this a correct way to use the Deferred object?

I'm building a App that need to list the categories and subcategories of a Product.
When a user selects a category, the subcategories related to this category are loaded from the server with ajax, but only if they have not been loaded later (in this case they are loaded from the DOM).
Code: http://jsbin.com/abijad/edit#javascript,html
var $form = $('#new-product-form');
$form.on( 'change', 'select.product-categories', function( e ) { //I'm using event delegation for a future feature...
getSubCategories( $(this).val() ).done( function( $subCategoriesEl ){
$form.find( 'select.product-subcategories' ).not( $subCategoriesEl ).hide();
$subCategoriesEl.show();
});
});
var getSubCategories = function( categoryId ) {
var dfd = $.Deferred(),
$alreadyisLoaded = $form.find( 'select.product-subcategories' ).map( function( idx, el ){
if( parseInt( $( this ).data( 'category' ), 10 ) === parseInt( categoryId, 10 ) ){
return el;
}
});
if( $alreadyisLoaded.length > 0 ){ //don't request subCategories that already are loaded
console.log( 'SubCategory loaded from DOM' );
dfd.resolve( $alreadyisLoaded );
} else {
var subCategoriesHtml = '<select data-category="' + categoryId + '" class="product-subcategories">';
$.get( '/', { //The ajax request will only be simulated
categoryId : categoryId
}, function ( result ) {
//simulate success :X
result = {"status":1,"data":[{"name":"Sub-Category I","id":1},{"name":"Sub-Category II","id":2},{"name":"Sub-Category III","id":3}]};
console.log( 'SubCategory loaded with Ajax' );
if( result.status === 1 ) { //Success
for( var subCategoryInfo in result.data) {
if( result.data.hasOwnProperty( subCategoryInfo ) ){
subCategoriesHtml += '<option value="' + result.data[subCategoryInfo].id + '">';
subCategoriesHtml += result.data[subCategoryInfo].name + '</option>';
}
}
subCategoriesHtml += '</select>';
var $subCategories = $( subCategoriesHtml ).hide().appendTo( $form );
dfd.resolve( $subCategories );
} else {
dfd.reject();
}
});
}
return dfd.promise();
};
<form id="new-product-form">
<select class="product-categories">
<option value="1">Category I</option>
<option value="2">Category II</option>
<option value="3">Category III</option>
<option value="4">Category IV</option>
<option value="5">Category V</option>
</select>
<select data-category="1" class="product-subcategories">
<option value="1">SubCategory I</option>
<option value="2">SubCategory II</option>
<option value="3">SubCategory III</option>
<option value="4">SubCategory IV</option>
<option value="5">SubCategory V</option>
</select>
</form>
Because the code was getting full of callback here and there, I decided to use the jQuery Deferred object, but I do not know if this is the correct implementation. Could someone tell me I did the right thing, or should I do differently?
I don't see anything glaringly incorrect. All in all, you are using the deferred in the correct fashion: to abstract away the possibly dual-synchronistic nature of your method. Now, that being said, if this were code appearing in my codebase, this is how I would write it. The main points being: don't use for in on arrays, build strings with arrays, consistent naming and spacing, and just some other terse JS preferences. These are a matter of taste, so otherwise, good job:
(function() {
var getSubCategories = function ( categoryId ) {
categoryId = +categoryId;
return $.Deferred( function ( dfd ) {
var isLoaded = form.find( 'select.product-subcategories' )
.map( function ( index, el ) {
if ( +$( this ).data( 'category' ) === categoryId ) {
return el;
}
}),
markup = [ ];
if ( isLoaded.length ) {
console.log( 'SubCategory loaded from DOM' );
dfd.resolve( isLoaded );
} else {
markup.push( '<select data-category="' + categoryId + '" class="product-subcategories">' );
var request = $.ajax({
url: '/',
data: { categoryId: categoryId }
});
request.done( function ( result ) {
//simulate success :X
result = {"status":1,"data":[{"name":"Sub-Category I","id":1},{"name":"Sub-Category II","id":2},{"name":"Sub-Category III","id":3}]};
var status = result.status,
data = result.data,
i = 0,
il = data.length,
current;
console.log( 'SubCategory loaded with Ajax' );
if ( status !== 1 || !data ) {
dfd.reject();
return;
}
for ( current = data[ i ]; i < il; i++ ) {
markup.push( '<option value="' + current.id + '">' );
markup.push( current.name + '</option>' );
}
markup.push( '</select>' );
dfd.resolve( $( markup.join( '' ) ).hide().appendTo( form ) );
});
}
}).promise();
};
var form = $( '#new-product-form' )
.on( 'change', 'select.product-categories', function ( e ) {
getSubCategories( $( this ).val() )
.done( function( el ) {
form.find( 'select.product-subcategories' )
.not( el )
.hide()
el.show();
});
});
});
As a side note, just wanted to bring up that you don't have any handling of problems if the ajax request fails. You would need to reject in that case, and make sure you write fail methods. Just a heads up.

Categories

Resources