I've created a site for sharing some old family letters. I'm using two Select2 controls to let people filter who wrote the letters and who received them. It's all working nicely, but I'd like to have each select2 get filtered based on what was selected in the other one. That is, if a user chooses a particular letter writer, then I'd like the select2 of recipients to show only people that letter writer wrote to. I've actually got that part working, but updating the contents of the dropdown removes any selections it had, and I can't get updating the selections to work.
A few notes and then I'll show some code and try to clarify what I'm seeing and trying.
Both select2 controls are set up for multiple selection.
There's a MySQL database that contains the information about what letters exist, who they were written by, and who they were sent to.
The HTML for the two select2 dropdowns is quite similar. Here's the code for the "From" dropdown:
<select id="from" class="js-example-basic-multiple" multiple="multiple" style="width: 100%">
<?php
echo GetPeople('F');
?>
</select>
GetPeople is a PHP function that calls out to the database and then converts the returned data to a series of lines. The parameter indicates whether to get the "From" data or the "To" data. That parameter is also used as part of the ID for each item (because it turns out that if options with the same ID appear in different select2 controls, things go downhill fast).
function GetPeople ($type) {
$people = getdata("GetPeople", "'$type',''", "Couldn't retrieve list of people.<br/>");
$return = '';
while ($obj = mysqli_fetch_object($people)) {
$return .= "<option value='" . $obj->FullName . "' id='" . $type . $obj->iid . "'>" . $obj->FullName . "</option>";
}
return $return;
}
The OnChange event for each of the select2 controls calls a JS function called updatePeople, passing either 'F' or 'T' to indicate which select2 needs its contents updated. That is, the From dropdown passes 'T' and the To dropdown passes 'F'.
updatePeople uses AJAX to go out and collect the data, build a new set of lines and sub them into the specified control. That's all working as expected. But doing so leaves that control with no items selected. When I try to restore the selections the control had before the update, nothing happens. Here's the code for updatePeople, including my attempt to restore the selection:
function updatePeople(cwhich) {
var results = '';
var success = false;
//get the currently selected list for the other dropdown, too
//so we can use them to restore selections later
if (cwhich == "F") {
var chosen = getChoices('#to');
var selecteditems = getChoices('#from',true);
} else {
var chosen = getChoices('#from');
var selecteditems = getChoices('#to',true);
}
var filters = cwhich + "','" + chosen + "'";
var fnargs = "GetPeople|'" + filters;
$.ajax({
url: 'retrievedata.php',
type: "POST",
async: false,
data: {"functionname": "getpeople", "arguments": fnargs},
dataType: "JSON",
success: function (obj) {
if (cwhich=="F") {
var target = "#from";
} else {
var target = "#to";
}
if ((obj===null)) {
//clear dropdown
$(target).html('');
}
else {
//build the list of options
var options = '';
for (line = 0; line < obj.length; line++) {
options += "<option value='" + obj[line].FullName + "' id='" + cwhich + obj[line].iid + "'>" + obj[line].FullName + "</option>";
}
window.allowupdate = false;
$(target).html(options);
//turn list of selected items into array
var arrSelected = selecteditems.split(',');
$(target).val(arrSelected);
$(target).trigger('change');
window.allowupdate = true;
}
success = true;
},
error: function (textStatus, errorThrown) {
success = false;
$("#testresult").text('Error occurred: '.textStatus);
console.log('Error occurred: '.textStatus);
}
});
}
The window.allowupdate bit is because trying to change the selections for one dropdown would fire its OnChange, and that would change the first one. So I added a flag that's checked in the OnChange code, so that I can change one without changing the other.
What I'm looking for is how to set the modified select2 control to have the same items selected as before updating the contents of the control. TIA
Solved my own problem. There were two issues:
Setting the control’s HTML seemed to make it unhappy. I changed that code to first disconnect from select2, then update the HTML, then reconnect to select2:
$(target).select2('destroy');
$(target).html(options);
$(target).select2({placeholder: 'Anyone (or choose senders)'});
I didn’t have the right value to reset the selections. My code was retrieving the primary keys for the selected items, but I needed their actual contents, which I was able to retrieve by calling the control’s val function.
Related
I need to use kendo-ui grid for data editing. Problem is that every possible item in returned response is string, but which contains other types of value (eg Value = "true" or Value = "32%" or Value = "[0:standard, 1:advanced]").
So I need to set up template on grid to correspond different data type within string.
So for true/false i have to have checkbox, for 32% it should provide text box but with percent validation, for array response it needs to be a drop down.
I managed to set up drop down and text box options using editor, but I cannot make checkbox to handle properly in any way. Checkbox is displayed as expected, but whatever I try it won't bind data to the grid after grid is saved. (it is always not checked, regardless of value)
Here is code snippet of column "value" and what I used for template (item.type === "3" is boolean).
field: 'value',
title: 'value',
headerAttributes: {
'class': 'table-header-cell'
},
template: function (item) {
if (item.type === "3") {
var boolValue = (/true/i).test(item.value);
item.value = boolValue;
return '<input id="' + item.name+ '" type="checkbox" #= value ? \'checked="checked"\' : "" # class="chkbx" />';
} else {
return ''; //this will follow for other types
}
},
Thanks in advance.
When the template definition is a function, you don't need to use the # markers to differentiate between markup and javascript like you do when you are defining a kendo template using kendo's template language or a string directly.
This is because inside the function it is always javascript and the # markers are only directives in the kendo templating language.
So, simplify your template to just:
template: function (item) {
return '<input class="chkbx" id="' + item.name + '" type="checkbox"' + (item.value ? 'checked="checked"' : '') + '/>';
}
I've left out the other datatype handling for simplicity.
Then, you need to add code to push the checkbox changes into the grid's datasource when they occur:
$("#grid").on("click", ".chkbx", function () {
var $checkBox = $(this),
checked = $checkBox.is(":checked"),
dataItem = grid.dataItem($checkBox.closest("tr"));
dataItem.set("value", checked);
});
This is a technique that I am currently using in production code.
Dojo example
It may also be possible to use the kendo MVVM bindings in your template for a more elegant solution instead of the explicit click handler, but I'd have to experiment more with that to figure it out.
In my KendoUI datasource, I have the following defined:
change: function (e) {
if (e.action === "itemchange") {
// auto-format Display As
var geoDisplay, geoUrl;
if (e.items[0].GeoState.length > 0) {
geoDisplay = e.items[0].GeoCity + ", " + e.items[0].GeoState;
} else {
geoDisplay = e.items[0].GeoCity;
}
//this.dataItem(this.select()).GeoDisplay = geoDisplay;
e.items[0].GeoCity = "updated: " + e.items[0].GeoCity; // visually updates if editing this field
e.items[0].GeoDisplay = geoDisplay; // field is not updated
}
console.log("change: " + e.action);
console.log(e);
// do something else with e
},
Essentially I want to update other fields on a row being edited based on a field's input.
In this example, GeoCity is updated. The itemchange event is fired and only the GeoCity field gets updated with the new value. However I can see from the data that the other fields' data have been updated.
I have tried doing a .sync() and a few other methods to get this to appear, but no luck so far.
Incidentally, my grid is defined within an AngularJS directive and it's onEdit event isn't what I'm looking for, as I want the events that fire when each field is updated, not the whole row.
How can I get the other fields to visually update?
I managed to solve the issue by placing the following code in the data source code:
change: function (e) {
if (e.action === "itemchange") {
// auto-format Display As
var thisRow = $("#accountGeoLocationEditorGrid tbody").find(".k-grid-edit-row");
// update geo display
if (e.field === "GeoCity" || e.field === "GeoState") {
var geoDisplay, geoUrl;
if (e.items[0].GeoState.length > 0) {
geoDisplay = e.items[0].GeoCity + ", " + e.items[0].GeoState;
} else {
geoDisplay = e.items[0].GeoCity;
}
if (e.items[0].GeoDisplay.length == 0) {
e.items[0].GeoDisplay = geoDisplay;
thisRow.find("input[name=GeoDisplay]").val(geoDisplay);
}
}
}
I was really looking for another way to do this, as I don't really want to be doing DOM lookup, etc in a defined datasource.
Other suggestions welcome.
Did you try the grid refresh() method? At the end of your changes in the change event, call this line (with your grid's correct id):
$("#grid").data("kendoGrid").refresh();
I've tested this on my grid and kendo's samples and it works fine like this. You are editing the datasource but the grid is not aware of the extra changes you have done, except the cell that was edited. Calling refresh will update all the cells on the grid to reflect the datasource.
Here is the code of my dropdown.
{
name: 'ClassId', index: 'ClassId', align: 'center',editable: true, edittype: 'select',
editoptions: {
dataUrl:'#Url.Action("GetAllClasses", "Class", new { Area = "Curriculums"})',
buildSelect: function (data) {
var response, s = '<select>', i;
response = jQuery.parseJSON(data);
//s += '<option value="0">--Select Class--</option>';
if (response && response.length) {
$.each(response, function (i) {
s += '<option value="' + this.Id + '">' + this.ClassName + '</option>';
});
}
return s + '</select>';
}
}
},
I am using form edit.I am reloading grid after insert.But the problem is after inserting data when I try to add another one my dropdowns are getting refreashed.I want that the dropdown selected value will be previously selected value.I don't want to change dropdown selected value on the second add.
The most simple way to prevent reloading of data from dataUrl could be setting Cache-Control HTTP header in the server response. Setting of Cache-Control: max-age=60 in the server response for example will prevent reloading of data from the server during 60 sec. In case of ASP.NET MVC you can use CacheControl attribute for example (see Duration and Location properties).
One more alternative would be dynamical setting editoptions.value instead of usage editoptions.dataUrl. For example one can include the information needed for building editoptions.value as an extension of the standard response of the server for filling the grid. One can use beforeProcessing to process the part of the data. You will find the corresponding examples in the following cold answers: this one, this one, this one, this one, this one and other. The answer describes in short one of the the possible scenario to create full dynamic grid.
I call the following function before the add dialog is shown; after clicking [+] on the JqGrid.
MVC Controller grid configuration
ordersGrid.ClientSideEvents.BeforeAddDialogShown = "initAddDialog";
The function makes an Ajax call to create a new order record either with or without a linking id, dependent on whether an existing order was selected when the [+] button was clicked.
The purpose is to make available the id necessary to make another Ajax call to retrieve additional linked information from another service, and to pre-populate the new record with date/time information and (where applicable) common information from an existing record.
function initAddDialog() {
var newOrderId = 0;
var newOrderLinkId = 0;
var selRow = jQuery('#clientOrderGrid').jqGrid('getGridParam', 'selrow');
var selRowData = jQuery('#clientOrderGrid').jqGrid('getRowData', selRow);
Get linking ID from selected row (if any)
var curOrderLinkId = (selRowData.OrderLinkId == null) ? 0 : selRowData.OrderLinkId;
Ajax call to create new 'Holding' Order
$.ajax({
url: '/Order/ajaxNewOrder?OrderLinkId=' + curOrderLinkId,
success: function (newOrderResponse) {
arr = newOrderResponse.split("|");
newOrderId = arr[0];
newOrderLinkId = arr[1];
},
error: function () { alert("There was an error creating an Order record"); }
});
If I break the code here using Firebug in Firefox, I can see that the variables newOrderId and newOrderLinkId have been set correctly with the id's from the newly created record, and if I hit F8 the (already displayed) dialog is populated with these //values.
If I don't break the code the dialog is displayed, but displays the values with which the variables were initialised i.e. newOrderId = 0, newOrderLinkId = 0.
$('#' + 'OrderId').val(newOrderId);
$('#' + 'OrderLinkId').val(newOrderLinkId);
$('#' + 'Stock').val(stock);
$('#' + 'SettlesTs').val(settlesTs);
$('#' + 'ReceivedTs').val(dtThis);
$('#' + 'ReceivedHHmm').val(dtTime);
I've tried calling the same function after the add dialog is shown, but get the same results.
Any thoughts as to why this is, or is there a better way of achieving the same result?
Thanks
I have taken the native add/edit dialog out of the equation and am using my own 'add' form along with the native inline editing of JqGrid. I'm sure with more research it would have been possible to find a solution, but with deadlines looming I had to find a workaround.
i have a database where each row has lat/long info for goggle maps.
each row subsequently gets turned into a marker. when i click on that marker, an info window pops up.
i want the info window to have a button so that when clicked, the entry from the database will get deleted. but my button won't work. specifically, something about the eraseEntry() function attached to the onClick event isn't working. when i click the button, only the last entry of the database gets deleted, regardless of which marker i click.
var map=//make the google map
var markersArray = [];
var infoWindow = new google.maps.InfoWindow({content: ""});
var markers;
$.get("phpsqlajax_genxml.php", function(data)
{
markers = data.documentElement.getElementsByTagName("marker");
makeMarkersWithXMLinfo();
});
function makeMarkersWithXMLinfo()
{
for (var i = 0; i < markers.length; i++)
{
var name = markers[i].getAttribute("name");
var markerLocation = new google.maps.LatLng(parseFloat(markers[i].getAttribute("lat")),parseFloat(markers[i].getAttribute("lng")));
var html = "<b>" + name + "</b> <br/>" +
"</b> <br/>" + "</b> <br/>" +
"<input type='button' value='Erase Entry' onclick='eraseEntry()'/>";
var markerWithLocation = new google.maps.Marker({position: markerLocation, map: map});
var markerWithInfo = createMarker(markerWithLocation,html);
eraseEntry = function ()
{
$.get("delete.php", { identifierVar: name } );
}
}
}
function createMarker(markerWithLocation, html) {
var markerWithInfo = google.maps.event.addListener(markerWithLocation, 'click', function(){infoWindow.setContent(html); infoWindow.open(map,markerWithLocation)});
return markerWithInfo;
}
i'm able to pull from the database and create the markers just fine.
i've tried having the eraseEntry() function call another function that's outside of the makeMarkersWithXMLinf() function, but i still get the same problem. depending where i put/call those functions, sometimes javascript doesn't even think my function exists...
below's my delete.php file
<?php
require("phpsqlajax_dbinfo.php");
// Opens a connection to a MySQL server
$connection = mysql_connect ('localhost', $username, $password);
if (!$connection) {
die('Not connected : ' . mysql_error());
}
// Set the active MySQL database
$db_selected = mysql_select_db($database, $connection);
if (!$db_selected) {
die ('Can\'t use db : ' . mysql_error());
}
if(isset($_GET['identifierVar']))
{
$query = 'DELETE FROM markers WHERE name = '.(int)$_GET['identifierVar'];
$result = mysql_query($query);
}
?>
i've been testing with just unique names.
like 111, 222, 333.. etc
any help's appreciated. thanks.
identifierVar should be identifierId, where identifierId is the id of that marker. try making these delete queries with the id, its much faster and it's a unique value.
your query is probably like this: delete from x where name = '$name'. you need to limit to 1, like: delete from x where name = '$name' limit 1
after you've successfully deleted the marker from database, delete it off the map
use firebug always in your debugging