QUESTION
Currently, I have a working ajax script which when a value in the first drop down is selected, it runs the ajax script to populate the second drop down. However, the second select does not actually run/populate until you click on that second drop down - the first time you click it, it will say "no results", and then you click on it again, and it loads the list, rather than populating immediately when the first drop down value is selected.
How might I go about making the second drop down populate immediately, or alternatively go about adding a spinner or disable pet_breed or something similar until the second drop down loads the values so 'no results' doesn't show after the type_taxonomy value has been selected?
I'm sorry but I have very little experience with javascript...
taxonomy setup
// Custom Taxonomy (First Select Box) - type_taxonomy
parent => false
values => Cat
Dog
// Custom Taxonomy (Second Select Box) - breed_taxonomy
parent => Cat
child => Cat Breed 1
Cat Breed 2
Etc...
parent => Dog
child => Dog Breed 1
Dog Breed 2
Etc...
existing code - audp-select-breed.js
(function( $ ) {
'use strict';
$(function($) {
$('#pet_type').on('change', function() {
var petType = $(this).val();
if ( petType != '' ) {
var data = {
'action': 'audp_select_pet_breed',
'pet_type': petType
}
$.post(audp_select_pet_breed_obj.ajaxurl, data, function(response) {
$('#pet_breed').html(response);
});
}
});
});
})( jQuery );
I just added disabled functions in the appropriate code lines to ensure that the select box could not be accessed until the data had loaded.
(function( $ ) {
'use strict';
$(function($) {
$('#pet_type').on('change', function() {
$('#pet_breed').prop('disabled', true); // Disables While Values Load
var petType = $(this).val();
if ( petType != '' ) {
var data = {
'action': 'audp_select_pet_breed',
'pet_type': petType
}
$.post(audp_select_pet_breed_obj.ajaxurl, data, function(response) {
$('#pet_breed').html(response);
$('#pet_breed').prop('disabled', false); // Enables After Values Load
});
}
});
});
})( jQuery );
Related
On a project I'm currently working on, I'm using radio buttons and AJAX to change the posts displayed on a custom WordPress template page. It works perfectly, however, the client would like it to be checkboxes instead of radio inputs so that each time a user selected a new category, it adds to the posts being displayed instead of replacing it.
For example: currently, if you click category1, category1 posts show up. Click category2, and category2 posts replace the category1 posts. The client would like BOTH category1 and category2 to show up if both checkboxes are selected.
Here's my JS currently:
jQuery(document).ready(function ($) {
// AJAX Post Filter scripts
var $checkbox = $("#filter input:checkbox");
var $checked = $("#filter input:checkbox:checked");
var $unchecked = $("#filter input:checkbox:not(:checked)");
$checkbox.change(function () {
if ($checked) {
var catID = $(this).val();
$.ajax({
type: 'POST',
url: afp_vars.afp_ajax_url,
data: {
"action": "load-filter",
category__in: catID
},
success: function (response) {
$(".filter-section").empty().html(response);
return false;
console.log('success!');
}
});
console.log(catID);
}
});
});
I'm pretty sure I need to do something with .map() with my variable catID as I've seen in some other threads, but I haven't been able to find a solution that works quite right for me.
edit: I realized I needed to clarify what I have tried.
I've used the following code:
var catID = $(this).map(function () {
return $(this).val();
}).get();
But all it does is replace the variable value with the new checkbox value. I need to add to the value without erasing the already checked values. Then if a checkbox is unchecked, I need to remove the value from the array.
I would try something like this:
let categoryIDs = [];
$('.checkbox').click((e) => {
let id = e.currentTarget.id
$(selector).html('')
categoryIDs.indexOf(id) === - 1 ? (
categoryIDs.push(Number(id))
) : (
categoryIDs = categoryIDs.filter((item) => item !== id )
)
console.log(categoryIDs)
categoryIDs.forEach((item) => {
//ajax calls here
//$.ajax({blah blah}).done(res => {
//$(selector).append(res)
//})
console.log(item);
})
})
See it in action here:
https://jsfiddle.net/mwypd9ve/
This way the IDs you want to display are always in the array, and every time the array changes the page will reflect only the content matching the IDs in the array (because you clear the section .html('') then in the loop append each ajax response
In my WordPress' projects, I'm using the following code again and again for many of my fields where I'm using a button to initiate the WordPress media uploader and on selection of the file I'm sending its path/url to a text field.
var project_field_image_uploader;
$('#button-input').click( function(e) {
e.preventDefault();
//if the uploader object has already been created, reopen the dialog
if( project_field_image_uploader ) {
project_field_image_uploader.open();
return;
}
//extend the wp.media object
project_field_image_uploader = wp.media.frames.file_frame = wp.media( {
title:"Choose an image",
button:{
text: "Insert"
},
multiple: false
} );
//when a file is selected, grab the URL and set it as the text field's value
project_field_image_uploader.on( 'select', function() {
attachment = project_field_image_uploader.state().get('selection').first().toJSON();
$('#text-field').val(attachment.url);
});
//Open the uploader dialog
project_field_image_uploader.open();
});
For each of the field I need to edit the following things:
First variable - project_field_image_uploader (not necessarily it should be meaningful, it is only for creating different instances, so in a reusable way, it can be anything, but not conflicting)
Button's ID - $('#button-input')
Text field's ID - $('#text-field')
Media Library Modal's head - title:"Choose an image",
Media Library's Media Insertion button's text - text: "Insert"
Is there a way I can make this code reusable, so that I can be with DRY ideology? A jQuery function may do the job for me, but I cannot sort things out, how can I sort this thing.
<script>
$(function(){
$('#button-input').click(function(e){
var text_field = $('#text-field');
....................
var mytext = 'my text';
myfunc(e,project_field_image_uploader,text_field,mytitle,mytext);
});
//reuse with any other button click with different parameters
});
function myfunc(e,project_field_image_uploader,text_field,mytitle,mytext){
e.preventDefault();
//if the uploader object has already been created, reopen the dialog
if( project_field_image_uploader ) {
project_field_image_uploader.open();
return;
}
//extend the wp.media object
project_field_image_uploader = wp.media.frames.file_frame = wp.media( {
title:mytitle,
button:{
text: mytext
},
multiple: false
} );
//when a file is selected, grab the URL and set it as the text field's value
project_field_image_uploader.on( 'select', function() {
attachment = project_field_image_uploader.state().get('selection').first().toJSON();
text_field.val(attachment.url);
});
//Open the uploader dialog
project_field_image_uploader.open();
}
</script>
Thanks to #alamnaryab for his answer that directed me to the right way (+1 for that). But passing a variable as a function parameter was problematic. It produces an error:
project_field_image_uploader is not defined
I figured out things that, a variable need not to pass as a function parameter to be unique, because a variable inside a function is a local variable. So I simply called the variable inside the function and reused the function multiple times. I'm here posting the working example code.
And declaring multiple variables, I used comma with a single var declaration. There's no need to repeat things. Thanks again to Mr. Alam Naryab.
<script>
$(function(){
$('#button-input').click(function(e){
var text_field = $('#text-field'),
media_lib_head = 'Choose an image',
btn_text = 'Insert';
//using the function where necessary
project_reusable_repeating_func( e, text_field, media_lib_head, btn_text );
});
});
/**
* Reusable function
* #author alamnaryab
* #link http://stackoverflow.com/a/32035149/1743124
*/
function project_reusable_repeating_func( e, text_field, media_lib_head, btn_text ){
//a variable that need not to be unique, because it's local
var project_field_image_uploader;
e.preventDefault();
//if the uploader object has already been created, reopen the dialog
if( project_field_image_uploader ) {
project_field_image_uploader.open();
return;
}
//extend the wp.media object
project_field_image_uploader = wp.media.frames.file_frame = wp.media( {
title: media_lib_head,
button:{
text: btn_text
},
multiple: false
} );
//when a file is selected, grab the URL and set it as the text field's value
project_field_image_uploader.on( 'select', function() {
attachment = project_field_image_uploader.state().get('selection').first().toJSON();
text_field.val(attachment.url);
});
//Open the uploader dialog
project_field_image_uploader.open();
}
</script>
I'm having a trouble when I'm trying to load the data of select.
Once the page is loaded, when I do my first click on the select it doesn´t show nothing, any data.
I close it and when I click again on the select it shows me the data.
http://jsfiddle.net/r3AA9/19/
Any suggestion?
HTML
<div>
<select data-bind="options: values, value: option, optionsCaption: 'Selecione...', event: { click: onClickSelect }" ></select>
<label data-bind="text: option"></label>
JS
var ViewModel = {
option : ko.observable(),
values : ko.observableArray()
};
ViewModel.onClickSelect = (function() {
//Simulate server side
setTimeout(function()
{
ViewModel.values(["one", "two", "three"]);
}, 2000);
});
ko.applyBindings(ViewModel);
Any suggestion?
there is a way to do this.
try this code
ViewModel.onClickSelect = function (v, e) {
var sel = e.target;
if (sel.childNodes.length === 1) {
sel.childNodes[0].innerText = 'Loading...'
setTimeout(function () {
sel.childNodes[0].innerText = 'Selecione...'
ViewModel.values(["one", "two", "three"]);
sel.blur();
v.openSelect(sel);
}, 2000);
}
};
//to open 'select' programmatically
ViewModel.openSelect = function (element) {
if (document.createEvent) {
var e = document.createEvent("MouseEvents");
e.initMouseEvent('mousedown', true, true, window);
element.dispatchEvent(e);
}
else if (element.fireEvent) element.fireEvent("onmousedown");
};
ko.applyBindings(ViewModel);
Demo JSFiddle
It's natural.
When you load the page for the first time, the values array is empty, so there are nothing to show in the dropdown. Then when you click on the drop down, you trigger the select, which invokes this code:
setTimeout(function()
{
ViewModel.values(["one", "two", "three"]);
}, 2000);
What this code does is, after waiting two seconds, it loads the 3 values in the drop down list. So, if you close the drop down list, and click on it again at least 2 seconds later, the options are there. If you do it quickly enough you'll realize that clicking again in the drop down before the 2 seconds go by, the select is already empty.
So the code is working perfectly, as expected. For your question it's impossible to know what you was trying to do.
I have a list of categories and when the client selects one from the list a new list is created below it with the children of that category, now i need to add another level (another list) but i'm not sure how.
This should work but i guess the script can't know if the element is there or not.
So far my JS looks like this:
<script>
// when the user clicks on a drop down item from the list it adds a new drop down list
$('#cat_select_1').on('change', function() {
// fetch second list from the other script
var fetchUrl = 'http://localhost/includes/ajax/list-of-cats.php?catid=';
var fetchCatId = $( "#cat_select_1" ).val();
// if the second list is there
if ($("#cat_select_2").length){
// replace it with the new one
$.get(fetchUrl.concat(fetchCatId)).done(function(data) { $("#cat_select_2").html(data); });
}
else {
// otherwise append this one
$.get(fetchUrl.concat(fetchCatId)).done(function(data) { $("#jumbocats").append(data); });
}
});
//list #2 (not working)
$('#cat_select_2').on('change', function() {
// fetch third list from the other script
var fetchUrl = 'http://localhost/includes/ajax/list-of-cats.php?catid=';
var fetchCatId = $( "#cat_select_2" ).val();
// if the third list is there
if ($("#cat_select_3").length){
// replace it with the new one
$.get(fetchUrl.concat(fetchCatId)).done(function(data) { $("#cat_select_3").html(data); });
}
else {
// otherwise append this one
$.get(fetchUrl.concat(fetchCatId)).done(function(data) { $("#jumbocats").append(data); });
}
});
</script>
It works for the first list but it doesn't work for the second list.
What am I missing?
You can't use direct events with elements that doesn't exist. You need to use delegated events to solve this
$(document).on('change', '#cat_select_2' function() { ... }
Where document can be replaced by any parent element that exist at that time.
Check on documentation for more details (section "Direct and delegated events")
I am looking to hide the Approve/Reject Buttons in the Details Page of a Fiori App based on certain filter conditions. The filters are added in the Master List view (Left hand side view) thru the view/controller extension.
Now, if the user selects certain type of filter ( Lets say, Past Orders) - then the approve/reject button should not be displayed in the Order Details Page.
This is how I have defined the buttons in the Header/Details view
this.oHeaderFooterOptions = {
oPositiveAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_APPROVE"),
id :"btn_approve",
onBtnPressed: jQuery.proxy(that.handleApprove, that)
},
oNegativeAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_REJECT"),
id :"btn_reject",
onBtnPressed: jQuery.proxy(that.handleReject, that)
},
However at runtime, these buttons are not assigned the IDs I mentioned, instead they are created with IDs of __button0 and __button1.
Is there a way to hide these buttons from the Master List View?
Thank you.
Recommended:
SAP Fiori design principles only talk about disabling the Footer Buttons instead of changing the visibility of the Button.
Read More here about Guidelines
Based on filter conditions, you can disable like this:
this.setBtnEnabled("btn_approve", false);
to enable again: this.setBtnEnabled("btn_approve", true);
Similarly you can change Button text using this.setBtnText("btn_approve", "buttonText");
Other Way: As #TobiasOetzel said use
this.setHeaderFooterOptions(yourModifiedHeaderFooterOptions);
you can call setHeaderFooterOptions on your controller multiple times eg:
//Code inside of the controller
_myHeaderFooterOptions = {
oPositiveAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_APPROVE"),
id :"btn_approve",
onBtnPressed: jQuery.proxy(that.handleApprove, that)
},
oNegativeAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_REJECT"),
id :"btn_reject",
onBtnPressed: jQuery.proxy(that.handleReject, that)
}
},
//set the initial options
onInit: function () {
this.setHeaderFooterOptions(this._myHeaderFooterOptions);
},
//modify the options in an event
onFilter : function () {
//remove the negative action to hide it
this._myHeaderFooterOptions.oNegativeAction = undefined;
this.setHeaderFooterOptions(this._myHeaderFooterOptions);
},
//further code
so by manipulating the _myHeaderFooterOptions you can influence the displayed buttons.
First, you should use sId instead id when defining HeaderFooterOptions, you can get the footer buttons by sId, for example, the Approve button.
this._oControlStore.oButtonListHelper.mButtons["btn_approve"]
Please check the following code snippet:
S2.view.controller: You have a filter event handler defined following and use EventBus to publish event OrderTypeChanged to S3.view.controller.
onFilterChanged: function(oEvent) {
// Set the filter value, here i use hard code
var sFilter = "Past Orders";
sap.ui.getCore().getEventBus().publish("app", "OrderTypeChanged", {
filter: sFilter
});
}
S3.view.controller: Subscribe event OrderTypeChanged from S2.view.controller.
onInit: function() {
///
var bus = sap.ui.getCore().getEventBus();
bus.subscribe("app", "OrderTypeChanged", this.handleOrderTypeChanged, this);
},
getHeaderFooterOptions: function() {
var oOptions = {
oPositiveAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_APPROVE"),
sId: "btn_approve",
onBtnPressed: jQuery.proxy(that.handleApprove, that)
},
oNegativeAction: {
sI18nBtnTxt: that.resourceBundle.getText("XBUT_REJECT"),
sId: "btn_reject",
onBtnPressed: jQuery.proxy(that.handleReject, that)
}
};
return oOptions;
},
handleOrderTypeChanged: function(channelId, eventId, data) {
if (data && data.filter) {
var sFilter = data.filter;
if (sFilter == "Past Orders") {
this._oControlStore.oButtonListHelper.mButtons["btn_approve"].setVisible(false);
}
//set Approve/Reject button visible/invisible based on other values
//else if(sFilter == "Other Filter")
}
}