Currently setting up a dropdown which gathers results from an API. I have set it up as follows:
$("#my-dropdown").dropdown({
minCharacters: 2,
apiSettings: {
cache: false,
url: getCorrectUrl(),
method: 'POST',
onResponse: function(users) {
var response = {
success: true,
results: []
};
$.each(users, function(index, item) {
response.results.push({
name: item.name,
value: item.id
});
});
return response;
}
}
});
And it is returning the correct data, just not in the form that I want. I would like to format the menu items how I want.
Specifically, what I'm currently getting is:
...but what I want is:
I have seen the answer to this question, which uses the onShow function to change the items into a custom form, but is manual manipulation of the DOM the only way to do it? Or is there a built in method to specify custom format menu items for use in a API-powered dropdown menu?
I have done this before, it involves defining a custom type of a template for generating the HTML for the results from the response.
I would go to dropdown.js in your Semantic/components directory and find:
$.fn.dropdown.settings.templates = { //line 3715 in version 2.2
you will find some predefined templates there for how the JSON is used to generate the results.
I didn't edit the code there, instead I copied the usual results template (templates.dropdown), tweaked the copied contained js to fit my results and created my own function from it in my own js file:
function addDropdownTemplate(name){
$.fn.dropdown.settings.templates[name] = function (response) {//use response
//use the edited version of the default template here
}
}
When you call the above function, you will add your own template for converting the response to the HTML results, to use it just specify the type of template you will use in the api settings:
apiSettings: {
//your other settings as in the question above
type: 'imageResult' //imageResult is an example, you can call it whatever
//you like as long as it's the same as what you passed
//to your function as the name parameter
}
I am looking at my code and I didn't even attach an onResponse event in the apiSettings, wasn't necessary.
Having the same problem, I came accross that post and tried to apply the best answer solution, but failed to make it work.
However, it led me to discover another way to achieve what you want to do, using the "onSuccess" callback under "apiSettings":
$('.player-dropdown.remote').dropdown({
on: 'click',
direction: 'downward',
allowAdditions: false,
allowReselection: true,
fireOnInit: false,
apiSettings: {
url: 'search?q={query}',
onSuccess: function(response) {
var html = '' ;
$('.player-member-menu').empty();
$.each(response.results, function(index, item) {
html += '<span class="select-player nowrap item " data-value="' + item.value + '">'
+ '<small class="description right-floated">' + item.description + '</small>'
+ '<span class="text">' + item.name + '</span>'
+ '</span>';
});
$(this).find('.player-member-menu').html(html) ;
},
},
minCharacters : 3,
searchDelay: 500,
duration: 500,
saveRemoteData: false,
filterRemoteData: false,
showOnFocus: true,
fullTextSearch: true,
});
In my example, the results array is formatted as asked in the docs, excepted that each item has 3 parameters instead of 2: name, value and description.
Related
I'll try to explain my problem as much as I can. I am trying to create, with select2 plugin, a way for the user to write some letters and on each letter pressed a call to an API that gives the first 20 results given by that API.
So I have my HTML select :
<select name="filtre_products[]" class="form-control products-select2" multiple>
</select>
And then my JS (it's commented) :
$(".products-select2").select2({
width: '100%',
closeOnSelect: false,
placeholder: '',
minimumInputLength: 3,
query: function (query) {
var data = {results: []}, i, j, s;
if(query.term != null) {
var ajax_r = [];
$.ajax({
url: 'ajax_products_recherche.php?limite=10&real_json=1&recherche='+query.term,
success: function(data_a){
//Getting the data
data_a = JSON.parse(data_a);
//Looping through the each data and putting them inside a table
data_a.forEach( (e) => {
ajax_r.push(e);
});
//Filtering the data to get from it the id and the text to be used
ajax_r.forEach( (e) => {
var tmp = e.split('-');
var id = tmp[0];
var name = tmp[2];
data.results.push({value: id, text: name});
});
query.callback(data);
}
});
}
},
//Sending the results to the function to modify the options as I please
templateResult: formatResult
});
And this is the formatResult function I use :
function formatResult(d) {
if(d.loading) {
return d.text;
}
// Creating an option of each id and text
$d = $('<option/>').attr({ 'value': d.value }).text(d.text);
return $d;
}
My problem is that select2 is supposed to be creating the options dynamically upon initialization and thus actually creating <li> out of the options and adding to them dynamically id's and such. But in the way I'm creating it, it's putting the options INSIDE the <li> tags which is not what I want, I want it to treat it as dynamic options like he does without the query call.
Some doc sources for you guys, it shows a part of what I'm trying to do, but the example shows how to show results from what we write, I want it to show from the api upon each click, and of course having multiple select added :
http://select2.github.io/select2/#data
I've read the documentation and found that the ajax support seems to fit your needs.
In your select2 options object, add an ajax object. Inside this ajax object :
define the regular parameters for an ajax call (url, datatype, delay, etc.)
in the data property, define function that returns the query parameters that should be added to the queryString
in the processResults property, define a function that processes the data returned by the ajax call and returns an object containing a results array (just like the data object you're already building).
Then, you can reuse your templating function.
Here's a working snippet with a random API. Please note the query's term doesn't impact the returned data.
$(document).ready(function () {
$(".products-select2").select2({
width: '100%',
closeOnSelect: false,
placeholder: '',
minimumInputLength: 3,
ajax: {
url: "https://jsonplaceholder.typicode.com/users",
dataType: 'json',
delay: 250,
data: function (query) {
// add any default query here
return query;
},
processResults: function (data) {
// Tranforms the top-level key of the response object from 'items' to 'results'
var results = [];
data.forEach(e => {
results.push({ id: e.id, text: e.name });
});
return {
results: results
};
},
},
templateResult: formatResult
});
function formatResult(d) {
if(d.loading) {
return d.text;
}
// Creating an option of each id and text
$d = $('<option/>').attr({ 'value': d.value }).text(d.text);
return $d;
}
});
<html>
<head>
<script src="https://code.jquery.com/jquery-3.3.1.min.js">
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.full.js"></script>
</head>
<body>
<select name="filtre_products[]" class="form-control products-select2" multiple>
</select>
</body>
</html>
For your ajax success call, do this or similar. i think you don't require such big code. below code snippet is from my working script.
success: function (data) {
var dbSelect = $('#ddlItems'); // id for Dropdown list
dbSelect.empty();
result = JSON.parse(data);
// Parse result object and create your array collection in ajax_r object
for (var i = 0; i < ajax_r.length; i++) {
dbSelect.append($('<option/>', {
value: ajax_r.item[i].Value,
text: ajax_r.item[i].Text
}));
}
}
I have Bloggers and Blogs. Each blog is associated to a blogger. Here is a picture of the Blog records:
Notice that the title attribute of Jennell's first blog has some javascript that could be triggered if not sanitized properly in certain situations.
Here is exactly one of those situations. I have two select Boxes:
For the first select box: the user picks a Blogger. When this happens:
an AJAX request gets sent to the server to grab all of the associated Blogs for that selected Blogger
the server grabs all the associated blogs to that blogger and sends them back to the requester
The response for the ajax request removes all options within the Blog select box
The response for the ajax request then adds in as options in that Blog select box all the blogs that were grabbed on the server.
The Blogger named Jennell has an associated blog with a title that contains a javascript hack. Thus: when the Jennell Blogger gets selected:
That hack within her associated Blog will get executed:
Here is the actual code for the AJAX request:
$("body").on('change', '[data-action="blogger_sel"]', function() {
var blog_sel = $(this).closest('[data-parent-for="blog_sel"]').find('[data-action="blog_sel"]');
$.ajax({
url: "/blogs",
type: "GET",
data: {blogger_id: $(this).val()},
success: function (data) {
blog_sel.children().remove();
$.each(data, function (index, value) {
/* DO NOT APPEND THIS WAY. VULNERABLE TO USER-ENTERED JAVASCRIPT EXECUTING */
blog_sel.append('<option value=' + value.id + '>' + value.title + '</option>');
});
}
})
});
The issue in the AJAX request is this part:
value.title
I need to sanitize that.
Question: Within the AJAX response: how can I sanitize value.title?
Set the text/values via attributes, not the html string.
var str = "<script>alert('x');<\/script>Test",
opt = $("<option></option>", { "text" : str, value : "foo" });
$("select").append(opt);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select></select>
In case anyone wants to see a code solution to the question:
$("body").on('change', '[data-action="blogger_sel"]', function() {
var blog_sel = $(this).closest('[data-parent-for="blog_sel"]').find('[data-action="blog_sel"]');
$.ajax({
url: "/blogs",
type: "GET",
data: {blogger_id: $(this).val()},
success: function (data) {
blog_sel.children().remove();
$.each(data, function (index, item) {
blog_sel.append($('<option>', {
value: item.id,
text : item.title
}));
});
}
})
});
I am working on wijgrid and I have given a radio button to html form.
I have used jquery to get the value of radio button and it is displying on the form but its not showing at grid.
I want to get the value of paramcode on selection of radio button and this value should display at wijgrid.My code is working fine and its showing the value but when I am saving the the data of form its not accepting radio button values inside the grid.
Please help ...Thanks Tina!!
This is my JSON (reformatted for legibility, but actually minified):
{
"jsonWrapperforGrid": {
"page": "1",
"total": "2",
"rows": [
{
"tenantId": 0,
"paramType": "UserGender",
"paramCode":"F",
"langCode":"en",
"paramValue":"Female"
},
{
"tenantId": 0,
"paramType": "UserGender",
"paramCode": "M",
"langCode": "en",
"paramValue": "Male",
"paramBlob": ""
}
]
}
}
This is my jQuery script:
<script type="text/javascript">
$.ajax({
url: "UserGender",
type: "GET",
dataType: "json",
contentType : "application/json",
success: function (responce) {
if (responce.jsonWrapperforGrid.rows.length > 0) {
$.each(responce.jsonWrapperforGrid.rows, function (i, entity) {
$('#gencom01').append(
$('<label />', { 'for': 'gender' + entity.paramValue,
'text': entity.paramValue }),
$('<input />', { 'id': 'gender' + entity.paramCode,
'type': 'radio',
'name': 'gender',
'value': entity.paramValue })
.click(function() {
var inputValue = $('input:radio:[name=+"gendernew"]:checked').val(entity.paramCode);
$("#gencom").val(entity.paramCode );
})
);
});
}
}
});
</script>
This is my HTML:
<body>
<div id="gencom01">
<td><input id="gencom" style="width:205px ;" name="gendernew">
</div>
</body>
Is it really all your code?
If you start making ajax calls at load, maybe it's something you could have handled on the server side? But you probably have your reasons.
First you need to use the $(document).ready(); event, you cannot start appending stuff to a tag that is probably not in your DOM yet.
Your if statement is useless if (responce.jsonWrapperforGrid.rows.length > 0) , your .each() loop will just not do anything if the length is 0, no need to test for that before.
Then even worse, you started declaring a .click() inside a .append(), while it might work, that looks a bit weird and can be source of many errors. It is usually easier to debug to keep your DOM manipulation and your events separate. And use .on(), more up to date.
I don't know your technology behind your AJAX call, but parsing your JSON into a real JS object can help : var jsonData = $.parseJSON(responce);
Thus, it's good practice to make as little .append() as possible, using it in a loop can take time. I'd advise saving your data in a variable and only at the end of your loop you can append everything.
And I don't know what this <td> is doing in your <div>.
Here is how your code could look like, I couldn't test anything since I don't know how your JSON looks like :
$(document).ready(function()
{
$.ajax({
url: "/mgw/pankanis/admin/SysParameter?Paramtype=UserGender",
type:"GET",
dataType: "json",
contentType : "application/json",
success: function (responce)
{
var jsonData = $.parseJSON(responce);
var toAppend = "";
$.each(jsonData.jsonWrapperforGrid.rows,function(i,entity)
{
toAppend += '<label for="gender'+entity.paramValue+'">'+entity.paramValue+'</label>';
toAppend += '<input id="gender'+entity.paramCode+'" type="radio" name="gender" value="'+entity.paramValue+'">';
});
$('#gencom01').append(toAppend);
$("#gencom01 input:not(#gencom)").on("click",function()
{
$("#gencom").val($(this).attr("id").substr(6)); // entity.paramCode
})
}
});
});
The data that is coming as part of ajax call from my business class is as follows:
[{
"user_action": "<button class='detail-icon' title='Vehicle Pass History'onclick='javascript:openPopUpWagonList('DIM 008065');'> <img src='/WIMS/images/icon_detail.png'></button>",
"VEHICLE_ID": "DIM 008065",
"setFlag": "<img src='/WIMS/images/wims-tick.gif'>",
"clrFlag": "<img src='/WIMS/images/wims-tick.gif'>",
"setDate": "31 Jul 2010 11:11",
"lastClearDate": "24 Aug 2010 07:26",
"noOfdays": "24",
"ownerCode": "",
"operatorCode": "",
"maintainerCode": "",
"severity10Pass": "~",
"plot": "<button class='detail-icon' title='Plot' alt='Detail'onclick='javascript:popUpStepChangeListGraph('DIM 008065');'> <img src='/WIMS/images/icon_detail.png'></button>",
"activeFlag": "1"
}, {
"user_action": "<button class='detail-icon' title='Vehicle Pass History'onclick='javascript:openPopUpWagonList('N 005276');'> <img src='/WIMS/images/icon_detail.png'></button>",
"VEHICLE_ID": "N 005276",
"setFlag": "<img src='/WIMS/images/wims-tick.gif'>",
"clrFlag": "",
"setDate": "31 Aug 2011 10:05",
"lastClearDate": "24 Mar 2011 10:45",
"noOfdays": "0",
"ownerCode": "",
"operatorCode": "",
"maintainerCode": "",
"severity10Pass": "~~~",
"plot": "<button class='detail-icon' title='Plot' alt='Detail'onclick='javascript:popUpStepChangeListGraph('N 005276');'> <img src='/WIMS/images/icon_detail.png'></button>",
"activeFlag": "1"
}]
Here I have taken only 2 records for convenient readability, but I am displaying more than 150 records of this kind.
Now my ajax call format from my jsp page is as follows:
$.ajax({
type: "POST",
url: f_reqAction, // url is set already
data : {maintainer:maintainerValue,show:showValue},
dataType:'json',
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:80000, /* Timeout in ms */
success: handleApplyEventResponse,
error: handleResponseError
});
function handleApplyEventResponse(response) {
// response is coming from my business class
$("#stepChangeData").jqGrid('clearGridData');
if(response.length > 0){
for(var i=0;i<response.length;i++){
jQuery("#stepChangeData").jqGrid('addRowData',i+1,response[i]);
}
$('#stepChangeData').setGridParam({rowNum:response.length});
}
}
When I want to remove the data by calling jQuery("#gridtabID").jqGrid('clearGridData'); a javascript error is coming prompting to stop running the script. but if the data size is small like 20 or 30 the problem does not come.
I understand populating response data by calling jqGrid('addRowData' inside for loop is not efficient when the data is this much large. but i could not find a jqgrid api which can add this response data directly in the grid without any looping.
And i could not create the buttons contained in my json data through formatter option of colModel as I found it extremely difficult to create that type of button by formatter.So I created it in my business ligic and sending it to jsp page with json data as part of the ajax respose.
So in the above context, I would have loved to have an api which could have set this json data into the grid at a shot (i.e without any looping). And I think this way this script problem could have been averted as well.
The best way to fill jqGrid with the JSON data from the server to use datatype: 'json' and load the data from the server. jqGrid is very flexible and one can load practically any data. One need just to use corresponding jsonReader or jsonmap property of colModel. Sometimes one need additionally use ajaxGridOptions option, serializeGridData or beforeProcessing callbacks. One should additionally use gridview: true option of jqGrid.
The difference to the usage of addRowData is huge. The main disadvantage of the usage of addRowData is that the data will be placed in the grid row per row. The problem is that if you modify an element on the page or insert a new one the position of all existing elements on the page have to be recalculated by the web browser. Because you use addRowData in the loop, then positing of the row which will be inserted first have to be recalculated also after any insertion of the next rows. So The more rows need be inserted the more slowly will be the code. Because of the problem jQuery 1.4 introduced the method jQuery.detach which allows to remove temporary an element from the page, modify it and then place it back with any standard jQuery methods like jQuery.after, jQuery.append and many other.
If you use datatype: 'json' and gridview: true the full body of grid will be constructed as string and then placed in the grid as one insert operation. So you will get much more better performance as with addRowData in case of many inserted rows.
UPDATED: On the demo I show how the loading of the data directly in jqGrid can be implemented. I recommend you to use predefined or custom jqGrid formatters. For example to be able correctly sort the data one have to include date in ISO 8601 format and use formatter: 'date' with the corresponding options to display the date.
Additionally I don't recommend you to use spaces inside of ID. I supposed that VEHICLE_ID is unique id of your data. I used it, but removed all spaces from the value.
About the usage function inside of postData I recommend you to read my old answer.
The most important part of the code of the demo you find below
var maintainerValue = 1, showValue = 200, $grid = $("#list");
$grid.jqGrid({
url: 'DebaprasadJana.json',
datatype: 'json',
mtype: "POST",
postData: {
maintainer: function () {
// if maintainerValue will be get from a field on the page
// one can get the data here directly like
// return $("#maintainer").val();
return maintainerValue;
},
show: function () {
return showValue;
}
},
loadonce: true,
gridview: true,
jsonReader: {
repeatitems: false,
id: function (obj) {
// we can use VEHICLE_ID as the rowid, but cut spaces
return obj.VEHICLE_ID.replace(/\s/g, '');
},
root: function (obj) { return obj; },
page: function (obj) { return 1; },
total: function (obj) { return 1; },
records: function (obj) { return obj.length; }
}
});
$("#reload").click(function () {
// next two lines simulate reading of the
// current values of maintainerValue and showValue
maintainerValue++;
showValue--;
// we need reset datatype only if we use loadonce:true
$grid.jqGrid('setGridParam', {datatype: 'json'})
.trigger('reloadGrid');
});
I have a page that I load a select dropdown box but I want to have javascript code toggle this list to a different set of choices.
I have the code that clears and repopulates a select list box in javascript
function UpdateDropdown(list, dropdownSelector) {
var options = '';
$.each(list, function (index, value) {
if (value.Text == null) {
value.Text = '';
}
if (value.Value == null) {
value.Value = '0';
}
options += '<option value="' + value.Value + '">' + value.Text + '</option>';
});
$(dropdownSelector).html(options);
}
so that is not the issue but my question is How can i store an array locally from my view Model to lookup later from javascript? Should i store it in a hidden select box? is there a recommended practice here?
The easiest and cleanest solution is to just write some json to your view, stored in a variable that you then access inside your UpdateDropdown function. In your view:
<script type="text/javascript">
var globalData = #Html.Raw(Json.Encode(Model.MyArray));
</script>
The exact method you use to output json will differ depending on the version of ASP.NET MVC you're using, but that should put you on the right track.
And general javascript best practice is to create a single namespace for your code that you then put all your other variables into, so that you don't pollute the global namespace. So really it should be something like:
<script type="text/javascript">
var MyApp = {};
MyApp.dropdownData = #Html.Raw(Json.Encode(Model.MyArray));
</script>
But the first block of code I provided would be fine to get started with.
leora,
I was inspired by the following solution for sending 'lists' from javascript to and mvc action:
var leadIds = $('input.chkTileSelector:checked').map(function () {
return { name: 'leadIds', value: $(this).parents('div.Lead').data('id') };
}).get();
$.ajax({
url: '#Url.Action("UnParkLeads", "Lead")',
type: 'POST',
dataType: 'json',
data: leadIds,
success: function (result) {
ReloadResultGrid();
}
});
you can see the article here:
http://zootfroot.blogspot.com/2011/09/jquery-post-javascript-array-to-aspnet.html