If else condition on dropdown list on MVC - c#

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.

Related

Returning a string of selected values from a checkbox collection

I am building a C# MVC view that includes a field with a series of 4 checkboxes. I can populate the list with the values and return individual values just fine, so the basics of the form appear to work. Here is how the options get populated on the view:
#for (int i = 0; i < Model.QuesOptions.Count; i++)
{
<div>
#Html.HiddenFor(x => x.QuesOptions[i].Name)
#Html.CheckBoxFor(x => x.QuesOptions[i].Checked)
#Html.LabelFor(x => x.QuesOptions[i].Checked, Model.QuesOptions[i].Name)
</div>
}
The ultimate goal is to return a single list of values (ideally comma delimited) of the values of each item checked at at the time of posting. So when the user clicks "submit" on the form, it would be great if a single field on the view model (called "UserAnswer") would populate with a comma delimited string. Is this even possible right on the view model?
I had hopes, probably empty ones, that some variation of this would work:
#Html.HiddenFor(model => model.Ques.UserAnswer,
new { Value = Model.QuesOptions.Where(x=>x.Checked).Select(x=>x.Name) })
Or would this be a job for some kind of extension method or HTML helper?
Finding no way to process comma delimited strings within Razor itself (a way may exist, but I haven't found it), I instead opted for JavaScript. The following code inserts comma delimited lists into hidden fields on the view model:
function submitForm() {
var question2 = "";
for (i = 0; i <= 3; i++) {
var trueOrNo = document.getElementById('Quez2Options_' + i + '__Checked').checked;
if (trueOrNo) {
question2 += document.querySelector('label[for="Quez2Options_' + i + '__Checked"]').innerHTML + ",";
}
}
document.getElementById('Quez2_UserAnswer').value = question2.substring(0, question2.length - 1);
var val3a = document.getElementById('Quez3a').value;
var val3b = document.getElementById('Quez3b').value;
var val3c = document.getElementById('Quez3c').value;
var question3 = val3a + "," + val3b + "," + val3c;
document.getElementById('Quez3_UserAnswer').value = question3;
}

get most recently selected listbox item from multiselect

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();
});

How to rename element ids to conform to model sequence collection

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';
});

Get text of the node

In an ASP.NET application I have a IreeView.
Here is one of the nodes in the view:
<td style="white-space: nowrap;">
<input id="TreeView1n10CheckBox" type="checkbox" checked="checked" name="TreeView1n10CheckBox">
<a id="TreeView1t10" onclick="TreeView_SelectNode(TreeView1_Data, this,'TreeView1t10');" href="javascript:__doPostBack('TreeView1','sPreAnalytical\\Test Requisitions\\2 Specimens: 1 Req')" class="TreeView1_0">2 Specimens: 1 Req</a>
As you can see it is a checkbox and there is text after it 'TreeView1','sPreAnalytical\\Test Requisitions\\2 Specimens: 1 Req'
How do I get the text 2 Specimens: 1 Req' on the client side, and how do I modify this text using JavaScript and display the modified TreeView to the client?
this works beautifully:
function check_OnTreeNodeChecked(event) {
var TreeNode = event.srcElement || event.target;
if (TreeNode.tagName == "INPUT" && TreeNode.type == "checkbox") {
if (TreeNode.checked) {
var elNode = document.getElementById("TreeView1t10");
var sText = elNode.innerText || elNode.innerHTML;
alert(sText);
elNode.innerHTML = "Whatever you want";
}
}
}
however since i need to modify the specific text next to the checkbox i need to be able to know which element id it was instead of implicitly specifying var elNode = document.getElementById("TreeView1t10");
Question how do i get the element id of the box that was checked?
The text can be retrieved using:
var elNode = document.getElementById("TreeView1t10");
var sText = elNode.innerText || elNode.innerHTML;
Modify it using:
elNode.innerHTML = "Whatever you want";
To get the ID of the tree node in your click handler:
From the top of my head, untested, something like this will get you the tree node from the checkbox ID:
Checkbox ID = "TreeView1n10CheckBox"; replace "CheckBox" with nothing, so we have
"TreeView1n10". Then replace the "n" with "t" and we have "TreeView1t10", which is
the ID of the corresponding anchor tag.
var sTreeID = TreeNode.id.replace("CheckBox", "").replace("n", "t");
var elTreeNode = document.getElementById(sTreeID);
With jQuery, it's quite simple...
var oldText = $('.TreeView1_0').text();
$('.TreeView1_0').text('new text here');
EDIT :
example here : http://jsfiddle.net/shaneblake/ZG888/
With a tree view the class is probably utilized multiple times so accessing the specific element would be of more use.
var oldText = $('#TreeView1t10').html();
If you need to update all the trees text you can loop through them pretty simply as well.
$('.TreeView1t10').each(function() {
var oldText = $(this).find('a').html();
});

handle multiple controls with same name in form collection

I have a form in my asp.net mvc(C#) application which handles some dynamic controls.
On a button click "Add Row", i will add a row dynamically to the existing table as:
$('#btnAddMore').click(function() {
var _userBodyHtml = '';
_userBodyHtml += '<tr><td><input type="text" name="UserName" id="UserName' + _userDynId + '" size="10" /></td>';
_userBodyHtml += '<td><textarea name="UserComments" id="UserComments' + _userDynId + '" cols="60" rows="1"></textarea></td>';
_userBodyHtml += '</tr>';
_userDynId += 1;
$('#UserBody').append(_userBodyHtml);
});
Then the admin adds the username and comments and submits it.
On submit, i am handling it in controller's action as:
var _frmUserNames = new List<String>(form["UserName"].ToString().Split(','));
var _frmUserComments = new List<String>(form["UserComments"].ToString().Split(','));
if (_frmUserNames.Count > 0 && _frmUserComments.Count > 0)
{
List<UserComments> _userComments = Enumerable.Range(0, _frmUserNames.Count)
.Select(i => new UserComments
{
UserName = _frmUserNames[i],
UserComment = _frmUserComments[i]
}).ToList();
}
From the above code, the _frmUserComments returns the comma separated value when there are more than one textbox with the same name as i am differentiating the textboxes only with different ids.
The problem is when the admin enters the usercomments which has a comma(,) within that comment, then the form value _frmUserComments has the comma separated value and it gives invalid data to the List.
When Admin enters(Case 1) which is fine:
Sam Logged on 12/10/2010
David Looking for enhancement
the form values returns:
_frmUserNames = "Sam,David"
_frmUserComments = "Logged on 12/10/2010,Looking for enhancement"
When Admin enters(Case 2) which is problem causing:
Sam Logged on 12/10/2010
David Logged on 03/01/2011, Looking for enhancement
the form values returns:
_frmUserNames = "Sam,David"
_frmUserComments = "Logged on 12/10/2010,Logged on 03/01/2011, Looking for enhancement"
How can i handle the scenario like this.
Looks like you want to bind collection to model. I recommend to do it as Phil Haack does
Try to set dynamic controls name attributes same as IDs (with index - yours's _userDynId). You'll be able to iterate through form collection in controller, something like that (using LINQ):
foreach (var key in form.AllKeys.Where(k => k.StartsWith("UserName")))
{
var index = key.Replace("UserName", "");
var userName = form[key];
var userComment = form["UserComments" + index];
}

Categories

Resources