I've got a listbox of items where I allow multi select.
I am trying to get the latest most recent item selected, not the order value, but what the user selected last.
If I try to print out .val() I get a list of the selected values, but I want the most recent (the last one in val()).
I tried using :last like so:
$("#MainContent_lbTeams option:selected:last").val();
But last works on order of ID's from ascending to descending, doesnt work on time when someone selects a value.
For instance if I have a list box of 4 items. If I select them in ascending order what I posted above prints correctly. But if I click the 1st, then the 4th, and back to the 2nd what I posted above prints the 4th item (even though I most recently selected the 2nd one).
//when a team is selected
//tell the user what this does to the record
$("#MainContent_lbTeams").on('change', function () {
//was a value selected?
var latest_value = $("#MainContent_lbTeams option:selected:last").val();
var latest_text = $("#MainContent_lbTeams option:selected:last").text();
alert(latest_value);
alert(latest_text);
if ($("#MainContent_lbTeams :selected").length > 0) {
$("#MainContent_lblTeamMembers").text("Members of '" + latest_text + "':");
// act only when the returned promise is resolved
PopulateMembers(latest_value).then(function () {
$("#MainContent_lbMembers_chosen a").removeClass("search-choice-close");
});
}
});
Is there something else I can use to pick up the latest selected value of my multiselect listbox?
Not sure if there is a better way but because it's multi you can find an array of objects for all the added ones in added and removed ones in removed.
$('select').on('change', function () {
var latest_value = $(this).data('value') || {},
latest_text = $(this).data('text') || {},
new_value = {},
new_text = {},
added = [],
removed = [];
$("option:selected", this).each(function(){
new_value[$(this).val()] = true;
new_text[$(this).text()] = true;
if($(this).val() in latest_value){
delete latest_value[$(this).val()];
delete latest_text[$(this).text()];
}else{
added.push({value: $(this).val(), text: $(this).text()});
}
});
for(v in latest_value){
removed.push({value: latest_value[v], text: latest_text[v]});
}
console.log('added', added);
console.log('removed', removed);
$(this).data({value: new_value, text: new_text});
});
DEMO
I ended up doing something a bit different.
Given that the .val() for a multi select is stored as an array, I took the difference between the two arrays. Here is what I ended up with:
var myTeam = [];
$("#trMembers").hide();
$("#MainContent_lbTeams").on('change', function () {
//was a value selected?
//store the difference of what teams are selected vs myTeam[]
var diff = [];
jQuery.grep($("#MainContent_lbTeams").val(), function (el) {
if (jQuery.inArray(el, myTeam) == -1) diff.push(el);
});
if (diff.length > 0) {
var latest_value = diff; //has the latest selected value
var latest_text = $("#MainContent_lbTeams option[value='" + diff + "']").text(); //stores the text
if ($("#MainContent_lbTeams :selected").length > 0) {
$("#dTeamNotice").show();
$("#MainContent_lblTeamMembers").text("Members of '" + latest_text + "':");
// act only when the returned promise is resolved
PopulateMembers(latest_value).then(function () {
$("#MainContent_lbMembers_chosen a").removeClass("search-choice-close");
$("#trMembers").fadeIn();
});
} else {
//hide it...
$("#dTeamNotice").css("display", "none");
$("#trMembers").hide();
}
}
myTeam = $("#MainContent_lbTeams").val();
});
Related
In my project for showing Grid data, I order by priority and user can change record Priority by arrow (down,up).
For Change Priority I use this code:
public virtual JsonResult ChangeOrder(int selectedCode, bool isUp)
{
var NewsObj = _newsService.Get(selectedCode);
if (NewsObj == null)
return Json(new { result = false, message = "error" });
int CurrentPriority = NewsObj.Priority;
int OtherPriority = 0;
if (isUp)
{
OtherPriority = CurrentPriority - 1;
}
else
{
OtherPriority = CurrentPriority + 1;
}
var OtherNews = _newsService.GetByPriority(OtherPriority);
if (OtherNews == null)
return Json(new { result = false, message = "error" });
int tmp = NewsObj.Priority;
NewsObj.Priority = OtherNews.Priority;
OtherNews.Priority = tmp;
_uow.MarkAllAsChanges(NewsObj);
_uow.MarkAllAsChanges(OtherNews);
_uow.SaveAllChanges();
return Json(new { result = true, message = "success" });
}
But this codes depends on ordering, for example when I use OrdeyByDesc this row does not work because -- and ++ should change.
I want to be independent of this.
I think you mix the meaning or Priority and Ordering.
Priority is normally not giving an order, because two or more elements can have the same Priority. It's giving only a kind of demand for this element.
An Order instead is lining up the elements in a sequence, so that each element has an unique Order Position. This can be done by using the Priority and/or other fields, may be something like a timestamp. Also it can be done ascendant or descendant, but that will not change the Priority.
So I would suggest to implement a functionality for changing the Priority. And separate, implement a second functionality for ordering, that can include an OrderByPriority. If someone changes the Priority of an element, just refresh the ordered list and the element will go up or down or wherever...
I want to make use of jqgrid / inline edit and want to:
inline edit the data
when done, user clicks on save and the (changed) data rows are send te the server
I have tried, but no results. Please see jsiddle here:
<script type="text/javascript">
function postAllGridData() {
// TODO : JSON request to server + reload grid
alert('code here');
}
</script>
http://jsfiddle.net/2Gc7K/
I modified your code to the following: http://jsfiddle.net/OlegKi/2Gc7K/2/.
In the code I set selected row in inline editing mode and save previous editing row (if any exist)
onSelectRow: function (rowid) {
if (rowid && rowid !== lastSel) {
$(this).jqGrid("saveRow", lastSel);
lastSel = rowid;
}
$(this).jqGrid("editRow", rowid, true);
}
After the user click on "save current grid data to server" the current editing row (if any exist) will be saved and then current data from the grid will be saved in the variables gridData. After then one can send the data to the server by separate jQuery.ajax call. In the simplest way I serialized the object to JSON string using JSON.stringify and displayed the results with respect of alert:
$("#postAllGridData").click(function () {
var gridData;
$grid.jqGrid("saveRow", lastSel)
gridData = $grid.jqGrid("getGridParam", "data");
alert("data:\n" + JSON.stringify(gridData));
});
I used rowNum: 10000. So that no local paging of data are used. The code will work in the same way even if local paging were used. In the case one should just specify the value of rowNum less as the number of rows and add top pager (by adding toppager: true option) or add empty <div> (like <div id="mypager"></div>) to the page and use pager: "#mypager" option.
UPDATED: One can modify the above code to the following
$("#postAllGridData").click(function () {
var gridData,
// get ids of edited rows
editedRows = $.map($grid.find(">tbody>tr[editable]"),
function(elem){
return $(elem).attr("id");
});
$grid.jqGrid("saveRow", lastSel);
alert("data:\n" + JSON.stringify(editedRows));
// get data of edited rows
gridData = $.grep($grid.jqGrid("getGridParam", "data"), function(row) {
return $.inArray(row.id, editedRows) >= 0;
});
alert("data:\n" + JSON.stringify(gridData));
});
(see http://jsfiddle.net/OlegKi/2Gc7K/5/). I used jQuery.map to get ids of rows which was edited (even non changed) and jQuery.grep to filter data to the rows which was edited.
The most common code will be
$("#postAllGridData").click(function () {
var gridData, indexes = $grid.jqGrid("getGridParam", "_index"),
idPrefix = $grid.jqGrid("getGridParam", "idPrefix"),
// get ids of edited rows
indexesOfEditedRows = $.map($grid.find(">tbody>tr[editable]"),
function(elem) {
return indexes[$.jgrid.stripPref(idPrefix, $(elem).attr("id"))];
});
// get data of edited rows
gridData = $.grep($grid.jqGrid("getGridParam", "data"), function(row, i) {
return $.inArray(i, indexesOfEditedRows) >= 0;
});
alert("data:\n" + JSON.stringify(gridData));
});
(see http://jsfiddle.net/OlegKi/2Gc7K/10/). Because we still use existence of editable attribute to filter the data it's important that the method works only in case of displaying all rows on one page. In case of paging one will have to save ids or indexes of edited rows on the current page before go to another page. One can use onPaging callback in the case. As the result we get the demo http://jsfiddle.net/OlegKi/2Gc7K/13/ with the following code
var lastSel, indexesOfOldEditedRows = [], $grid = $("#list4");
$grid.jqGrid({
...
onPaging: function () {
var $self = $(this),
indexes = $grid.jqGrid("getGridParam", "_index"),
idPrefix = $grid.jqGrid("getGridParam", "idPrefix"),
indexesOfEditedRows;
$self.jqGrid("saveRow", lastSel);
indexesOfEditedRows = $.map($self.find(">tbody>tr[editable]"),
function(elem) {
return indexes[$.jgrid.stripPref(idPrefix,
$(elem).attr("id"))];
});
$.merge(indexesOfOldEditedRows, indexesOfEditedRows);
}
});
$("#postAllGridData").click(function () {
var gridData, indexes = $grid.jqGrid("getGridParam", "_index"),
idPrefix = $grid.jqGrid("getGridParam", "idPrefix"),
indexesOfEditedRows;
// get ids of edited rows
$grid.jqGrid("saveRow", lastSel);
indexesOfEditedRows = $.map($grid.find(">tbody>tr[editable]"),
function(elem) {
return indexes[$.jgrid.stripPref(idPrefix, $(elem).attr("id"))];
});
$.merge(indexesOfOldEditedRows, indexesOfEditedRows);
// get data of edited rows
gridData = $.grep($grid.jqGrid("getGridParam", "data"), function(row, i) {
return $.inArray(i, indexesOfOldEditedRows) >= 0;
});
alert("data:\n" + JSON.stringify(gridData));
});
C# MVC web application
I have a button that adds input fields dynamically to my web page on a form.
I specifically give the dynamically added elements an id and name to conform to my Model so that they get passed back as an collection of like items to match a “Artists” collection property in my model.
So the input elements name and ids are for example
Artists[0].artist
Artists[1].artist
Artists[2].artist
In my model I have:
public Collection<Artist> Artists { get; set; }
public class Artist
{
public string artist { get; set; }
}
And here is my script to add remove an element:
var artists = new Array();
var AddArtist = function (e) {
artistname = $("#artistinput").val();
artists.push($("#artistinput").val());
var addDiv, artistVal;
addDiv = $("#artist");
artistVal = $("#artistinput").val();
var input_append = $('<div />'),
label = $('<input />', {
style: 'background-color:#e0ffff',
Value: artistVal,
id: 'artists[' + (artists.length - 1) + '].artist',
name: 'artists[' + (artists.length - 1) + '].artist',
readonly: true
}),
image = $('<img />', {
id: 'removeartist',
src: '/Content/bootstrap/img/cross-button.png',
on: {
click: function () {
input_append.remove();
artists.splice(artists.length - 1, 1);
var test = (artists.length - 1);
alert(test);
}
}
}
);
addDiv.append(input_append.append(label, image));
};
I can add/remove the elements on the actual page. The problem is, if I remove an element from the middle or beginning, the sequence of the name ids are broken and when the collection get passed back to my model the Artists collection is now empty.
So instead of
Artists[0].artist
Artists[1].artist
Artists[2].artist
This may be passed back:
Artists[0].artist
Artists[2].artist
Which is no longer a collection based on how the view is mapped to the model.
I need to rename all the name/ids in a ordered sequence once an item has been removed.
What’s the easiest solution for this problem.
So that this gets passed back
Artists[0].artist
Artists[1].artist
Okay here's what I used in a previous project to revise the IDs and names of inputs to allow model binding when posted to an MVC controller.
The first function takes an object and searches for all inputs, selects and textareas contained within it. The object you pass would probably be a row or div that contains all related inputs per Artist.
// Applies new id and name with correct number sequence to set fields
function reviseFieldNameAndId(obj, newNumber) {
obj.find('input,select,textarea').each(function () {
var parts = this.id.split(/_[\d+]__/); // Everything can be obtained from id only
this.id = parts[0] + '_' + newNumber+ '__' + parts[1]; // Payments_0__PaymentReasonId
this.name = parts[0] + '[' + newNumber+ '].' + parts[1]; // eg. Payments[0].PaymentReasonId
});
}
function reviseAllFieldNamesAndIds() {
$('#artists .row').each(function (index) {
reviseFieldNameAndId($(this), index);
});
}
Use the second function to go through all rows and perform the apply the new sequence.
PS - your answer is near enough the same as this but only works with artists whereas mine will work with different names and ids
I gave each dynamically added input item the same class name then used JavaScript to update and reorder every element id and name in an ordered sequence:
$('.divArtist').each(function (i, obj) {
this.id = 'artists[' + i + '].artist';
this.name = 'artists[' + i + '].artist';
});
I have a two dropdown list on my web application. The populated data of two dropdown comes from a database. They contain the same data.
What I want is if one of the list is selected on the first dropdown, it should not be available on the second dropdown anymore.
I have the following razor syntax:
#Html.DropDownListFor(model => model.Questions1, (SelectList)ViewData["Questions"], "Select>>", new { id = "Questions1", Name = "Questions1"})
#Html.DropDownListFor(model => model.Questions2, (SelectList)ViewData["Questions"], "Select>>", new { id = "Questions2", Name = "Questions2"})
The Questions came from the model which is retrieve from the database.
Thanks in advance!
Not sure if there's a slicker way to do this, but here's what I came up with.
Clone the option elements
Add change listener
Re-create options in #2
Remove the one selected in #1 from #2
// save all options locally
var options = $("#Question2 option").clone();
function removeItem() {
// clear and re-populate all options
$("#Question2 option").remove();
$("#Question2").append(options);
// get the selected option in #1 and remove from #2
var selected = $("#Question1 :selected").val();
$("#Question2 option[value='" + selected + "']").remove();
}
// update #2 on startup, and on change
$("#Question1").on("change", removeItem);
removeItem();
Fiddle
In order to accomplish this, you will need to store the pool of options in a javascript object. Then, in the 'onchange' event for each drop-down, re-build the options in the other drop-down, excluding the one chosen. Here is an example using jQuery:
// Build a javascript array with all of the select names/values
var options = new Array();
$('#Questions1 option').each(function() {
$this = $(this);
options.push({ Name: $this.text(), Value: $this.val() });
});
// Create a function for re-building a select minus the chosen option
var rebuildSelect = function($selOption, $select) {
$previouslySelected = $select.find(':selected');
$select.empty();
for (var i = 0; i < options.length; i++) {
var opt = options[i];
if (opt.Value != $selOption.val()) {
if ($previouslySelected.val() == opt.Value) {
$select.append('<option value="' + opt.Value + '" selected="selected">' + opt.Name + '</option>');
}
else {
$select.append('<option value="' + opt.Value + '">' + opt.Name + '</option>');
}
}
}
}
// Wire up the event handlers
var $Questions1 = $('#Questions1');
var $Questions2 = $('#Questions2');
$Questions1.change(function() {
rebuildSelect($(this), $Questions2);
});
$Questions2.change(function() {
rebuildSelect($(this), $Questions1);
});
// Go ahead and run the function on each box to remove the default entries from the other box
rebuildSelect($Questions1.find(':selected'), $Questions2);
rebuildSelect($Questions2.find(':selected'), $Questions1);
http://jsfiddle.net/n5k99/
You can use a Jquery function
So if you got 2 dropdowns
<select id="dd1" style="width:200px;">
<option value="feedback" name="aft_qst">After Quest</option>
<option value="feedback" name="aft_exm">After Exam</option>
</select>
<select id="dd2" style="width:200px;">
<option value="feedback" name="aft_qst">After Quest</option>
<option value="feedback" name="aft_exm">After Exam</option>
</select>
Then just add this jQuery
$("#dd1").change(function(){
$("#dd2").prop("disabled", true);
});
Look here: http://jsfiddle.net/tft4t/186/
This is not a direct answer, but more an alternative;
Why not keep both options in the drop down list and let the user select the same item, and when they do, display a message like "Please select two different items" etc. ? Your code should be easier too.
If I were a user, and for whatever reason, I wanted to select the same thing twice, I think I would rather the system let me do it and tell me afterwards that I did it wrong, than to try to select the same thing twice and find the item I want in the second drop down is missing, causing me a moment of panic.
My Ajax block looks like so:
$('#btnTest').click(function(){
$.getJSON('/User/ViewMessages',function(result) {
// TODO: update the DOM with the items
$("table#tblMessages").append("<tr><td>"+result.MESSAGETEXT+"</td><td>"+result.DATESENT+"</td>");
});
});
My Action in my Controller looks like such:
public ActionResult ViewMessages()
{
var recipient = Convert.ToInt32(HttpContext.Session["User_Id"]);
var query = (from m in context.Messages
from rec in context.Recipients
where rec.RECIPIENT == recipient
where rec.MESSAGEID == m.MESSAGEID
select new
{
m.MESSAGETEXT,
m.DATESENT
}).ToList();
return Json(query.ToList());
}
When Debugging, my query variable returns:
{ MESSAGETEXT = "seresr", DATESENT = {9/15/2011 11:06:45 AM} }
The thing is, my table is added with "undefined" for both my values. Where have i gone wrong? I have already added the maproute as well, but I'm still out of luck.
It looks like you're returning a list, which will be represented in JSON as an array. So your result object is a JavaScript array rather than a single object. To loop through all the items and add a table row for each, try something like this:
$.getJSON('/User/ViewMessages', function(result) {
for (var i in result) {
var row = result[i];
$("table#tblMessages").append("<tr><td>" + row.MESSAGETEXT
+ "</td><td>" + row.DATESENT + "</td>");
}
});