ASP.Net MVC 3 ListBox Selected Items Collection Null - asp.net

I have a pretty simple scenario and I'm sure I'm just missing something obvious. I'm trying to use a ListBox to grab multiple Id's and add them to my model, but no matter what I do, the collection is always null. Here's the code:
The model collections:
public IEnumerable<Model.UserProfile> TravelBuddies { get; set; }
public IEnumerable<int> SelectedTravelBuddies { get; set; }
I populate the TravelBuddies collection in my controller.
The view code:
<div class="module_content">
#if (Model.TravelBuddies.Count() > 0)
{
#Html.ListBoxFor(m => m.SelectedTravelBuddies, new MultiSelectList(Model.TravelBuddies, "Id", "FullName"))
}
else
{
<span>You don't currently have any travel buddies (people who were with you on this trip). Don't worry, you can add some to this trip later if you'd like.</span>
}
</div>
The select list is populated in my view. No problem there. But once I select multiple items and submit my form, the Model.SelectedTravelBuddies collection is always null. Am I missing something obvious? It's been a long night of coding.
Update: Added Controller Code
[HttpGet]
public ActionResult New()
{
Model.Trip trip = new Model.Trip();
ITripService tripService = _container.Resolve<ITripService>();
IUserAccountService userService = _container.Resolve<IUserAccountService>();
int userProfileId = userService.GetUserProfile((Guid)Membership.GetUser().ProviderUserKey).Id;
trip.TripTypes = new SelectList(tripService.GetTripTypes(), "Id", "Name");
trip.TravelBuddies = userService.GetTravelBuddies(userProfileId);
tripService.KillFlightLegTempStorage();
return View(trip);
}
[HttpPost]
public ActionResult New([Bind(Exclude = "TripTypes")] Model.Trip trip)
{
ITripService tripService = _container.Resolve<ITripService>();
if (!ModelState.IsValid)
{
tripService.KillFlightLegTempStorage();
return View(trip);
}
int tripId = tripService.CreateTrip(trip, (Guid)Membership.GetUser().ProviderUserKey);
tripService.KillFlightLegTempStorage();
return RedirectToAction("Details", "Trip", new { id = tripId });
}

Ok so you are binding to SelectedTravelBuddies. When your list is rendered, what is it's name? It's been a long night for me too :) want to make sure it matches the model. Also are you sure the list is in the form element so they are posted?

Related

Dynamically assign value to a Property

I have hard time understanding assigning value to a property dynamically, means during run time so that i can retrieve/display value in a razor page. I have following programming logic to accomplish my task, however this (LmitedWords) property does not render or hold any value to be displayed. How do I assign a value to this property during run time.
public class Post
{
public string Content { get; set; }
[NotMapped]
public string LimitedWords { get; set; }
}
My controller code follow:-
public async Task<IActionResult> GetAllPosts()
{
var myLimitProperty = new Post();
var result = await _repository.GetAllPosts();
foreach (var post in result)
{
var limitContent = ContentExtension.ReturnLimitedDescription(post.Content, size);
myLimitProperty.LimitedWords = limitContent;
}
return View(result);
}
my contentextension helper method returns value as expected and during debug it does show that local variable "limitContent" has the value but it somehow does not assign it to LimitedWords property, which is a property in Post class.
In my Post class there are other properties as well and i want them to be displayed as it is saved in the database.
My Razor page does not display content as it is null:
<div>
<markdown markdown="#Model.LimitedWords">
</div>
Thanks!
Well based on what you have posted, the result holds the posts returned by the repository.
You loop through these posts, update the myLimitProperty local variable in the action and return the original collection.
Nothing is actually being updated on objects being sent to the view
Create a projection from the list, populating the desired properties that should be displayed in the view.
public async Task<IActionResult> GetAllPosts() {
var posts = await _repository.GetAllPosts();
var result = posts.Select(post => {
var limitContent = ContentExtension.ReturnLimitedDescription(post.Content, size);
var model = new Post() {
Content = post.Content;
LimitedWords = limitContent;
};
return model;
}).ToList();
return View(result);
}

asp.net mvc form includes for loop over form controllers

i want to submit a form with for loop inside of it that loops over the form controllers since i want to pass list of model into a post method action in such a way that each and every form controller ( TextBoxFor ) is populated by corresponding value of my list of model
my Model :
public class ExcelRowData
{
public string LastName { get; set; }
public string FirstName { get; set; }
public string Score { get; set; }
public string EnrollmentTime { get; set; }
public string GraduationTime { get; set; }
}
and this is my view :
#model List<NewTest.Models.ExcelRowData>
#using (Html.BeginForm("Delete", "MyTest", FormMethod.Post))
{
for (var i = 0 ; i < Model.Count ; i++)
{
<tr>
<td>#Html.TextBoxFor(p => Model[i].LastName)</td>
<td>#Html.TextBoxFor(p => Model[i].FirstName)</td>
<td>#Html.TextBoxFor(p => Model[i].Score)</td>
<td>#Html.TextBoxFor(p => Model[i].EnrollmentTime)</td>
<td>#Html.TextBoxFor(p => Model[i].GraduationTime)</td>
<td><input type="submit" formaction="#Url.Action("Delete", "MyTest", new {Ln = Model[i].LastName})" value="Delete"/></td>
</tr>
}
}
i populated my model from excel file then i show the excel data in above table in view and then there is a delete button in case of deleting a record
and in my delete action i delete the selected record by the parameter passed from submit button , after that i repopulate my model and again i pass it to the same view
this is the delete action in my controller :
[HttpPost]
public ActionResult Delete(ExcelRowData evm, string Ln)
{
var exceldata = new List<ExcelRowData>();
var del = evm.FirstOrDefault(p => p.LastName == Ln);
evm.Remove(del);
foreach (var item in evm)
{
exceldata.Add(
new ExcelRowData { LastName = item.LastName, FirstName = item.FirstName, Score = item.Score, EnrollmentTime = item.EnrollmentTime, GraduationTime = item.GraduationTime}
);
}
return View("ExcelTable", exceldata);
}
the problem is when i press delete button in any row , it delets the record from bottom of the list , it doesn't matter which row i click to delete , it deletes from bottom of the list.
You posting back the whole collection, so all the property values have been added to ModelState, and the HtmlHelper methods that generate form controls (except PasswordFor()) use the values from ModelState (if they exist) rather the the property value.
You can solve this by adding
ModelState.Clear();
before you return the view, however the correct approach is to follow the PRG (Post, Redirect Get) pattern, and redirect to your GET method.
To understand why the HtmlHelper methods use ModelState, refer this answer.
As a side note, you can improve performance by using ajax to remove the item (and if successfully deleted, remove the corresponding row from the DOM), but that means you also need to include a hidden input for the collection indexer so that you can post back non-zero/non-consecutive indexers if your also posting back the updated collection elsewhere in your view. The input would be <input type="hidden" name="Index" value = "#i" />

DropDownList SelectedItem Not Working (Razor ASP MVC5)

Model
public class MyModel {
public string ItemXYZ { get; set; }
}
Controller
public ActionResult Edit(int? id)
{
var x = db.XYZs.Find(id);
ViewBag.Item_XYZ = new SelectList(new[] { "X", "Y", "Z"}, x.CurrentXYZ);
}
View
#Html.DropDownList("XYZ123", #ViewBag.Item_XYZ as SelectList, new { #class = "special-class" })
Problem
If I change my DropDownList to mach the name of the get set in my model, the selected value does not work.
#Html.DropDownList("ItemXYZ" ....)
If it matches the name of my ViewBag item, the SelectedItem doesn't work.
#Html.DropDownList("Item_XYZ" ....)
But, if I append 123 (for example), SelectedItem works just fine.
UPDATE
This is the same issue I am having, but I don't understand why or how to handle the return in my controller?
In Controller use the code as::
public ActionResult Edit(int? id)
{
var x = db.XYZs.ToList();
ViewBag.TestDropdown = new SelectList(x, "ValueField", "TextField", "DefaultSelected");
}
and in Client side to show Dropdown use this:
#Html.DropDownList("TestDropdown")
thats it.
May be this will help you.
Receive a parameter named XYZ123 in respective action.
Also include XYZ123 in Bind attribute of action.

Why Is The Selected Value Different Between Using Route ID and Action Parameter in SelectList

I have a SelectList in my action method. The selected value for SelectList is coming from the action method parameter. The action and view are simple like below:
// Recipe Action
public ActionResult Recipe(int? recipeId)
{
ViewBag.RecipeID = new SelectList(_recipsRecipes, "RecipeID", "RecipeName", recipeId);
return View(new Recipe());
}
//Recipe View
#model RecipeDemo.Models.Recipe
#Html.DropDownList("RecipeID", (SelectList)ViewBag.RecipeID, string.Empty)
I'm using ActionLink below to call the Recipe action.
#Html.ActionLink("Recipe", "Recipe", "Home", new { recipeId = 2 }, null)
It works like I expect, the DropDownList is showing the selected value as the No. 2 (recipeId = 2) item.
Problem
When I change the Recipe action parameter by using route id, like below:
//Recipe View
public ActionResult Recipe(int? id)
{
ViewBag.RecipeID = new SelectList(_recipsRecipes, "RecipeID", "RecipeName", id);
return View(new Recipe());
}
//Recipe View (Same View as above)
#model RecipeDemo.Models.Recipe
#Html.DropDownList("RecipeID", (SelectList)ViewBag.RecipeID, string.Empty)
And I'm using ActionLink below to call the Recipe action.
#Html.ActionLink("Recipe", "Recipe", "Home", new { id = 2 }, null)
The DropDownList is NOT showing the selected value, (id = 2) item. The selection is instead empty.
But I have the correct id value in the SelectList. see below:
Why is this, does anyone know the explanation?
Update:
The model is below:
public class Recipe
{
public int RecipeID { get; set; }
public string RecipeName { get; set; }
}
Well that was certainly interesting. After first confirming the issue with the code you provided, I experimented around and believe I have the root cause. Basically, you are using the same variable name way to often and the model binder appears to be getting confused. You have RecipeId in your route, RecipeId in your View Model and RecipeId as the name of your view bag variable. By altering my variable names, the SelectList works as expected.
The primary issue is naming your SelectList RecipeId which matches a property in your model. When you send the new Recipe(), the model binder is attempting to use that value. In your first example, since you have RecipeId defined in the URL, it is getting it from there. In the second example, there is no RecipeId to pull from the URL and it is null in the model.
Controller
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
private List<Recipe> Recipes;
public HomeController()
{
Recipes = new List<Recipe>
{
new Recipe {RecipeId = 1, RecipeName = "Test - 1"},
new Recipe {RecipeId = 2, RecipeName = "Test - 2"},
new Recipe {RecipeId = 3, RecipeName = "Test - 3"},
};
}
public ActionResult Index(int? id)
{
ViewBag.MyList = new SelectList(Recipes, "RecipeID", "RecipeName", id);
return View(new Recipe());
}
}
}
Index View
#model MvcApplication1.Models.Recipe
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.DropDownList("MyRecipeId", (SelectList)ViewBag.MyList)
Basically, vary your parameter names a little bit more to help prevent the model binder from getting confused and/or pulling information from the wrong place.
You can verify this in your second example by sending this in your return statement:
Return View(New Recipe{RecipeId = 3});
The option value with 3 will be selected regardless of what the actual Id sent was.
EDIT
An even better option would be to do what I said to do as an example above. By changing your Action to this:
public ActionResult Index(int? id)
{
ViewBag.MyList = new SelectList(Recipes, "RecipeID", "RecipeName");
return View(new Recipe(RecipeId = id));
}
You can leave your view unchanged. Now, the SelectList will pull from the model that you are sending.

ajax paging asp.net mvc

I know how to hook up ajax paging to a grid or a webgrid in asp.net mvc. But how can I accomplish ajax paging, using custom paging for large data sets for another format outside of a table grid.
Is that even possible using an mvc helper or mvc.pagedlist?
I used to be a webforms guys and it was so easy to hook up a listview where you could use divs to create whatever layout you want for individual items, you could then hook up a datapage and wrap it all in an update panel.
Basically I want a list of items that I can page through via ajax but with having large data sets I can just pull down all the items and page via jquery, I need to do custom paging on the server side and only return the items for a specific page.
By reusing a partial view and some ajax, this is very easily done in MVC.
Add this model as a property to your page's ViewModel to handle the pagination:
namespace Models.ViewModels
{
[Serializable()]
public class PagingInfoViewModel
{
public int TotalItems { get; set; }
public int ResultsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages {
get { return Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(this.TotalItems) / this.ResultsPerPage)); }
}
public string LinkTextShowMore { get; set; }
public string LinkTextShowingAll { get; set; }
/// <summary>
/// Paging url used by the jQuery Ajax function
/// </summary>
public string UrlGetMore { get; set; }
public PagingInfoViewModel(string linkTextShowMore, string linkTextShowingAll, int resultsPerPage)
{
this.LinkTextShowMore = linkTextShowMore;
this.LinkTextShowingAll = linkTextShowingAll;
this.ResultsPerPage = resultsPerPage;
}
}
}
Add the following code to your partial view to handle the pagination:
//Start Pagination
//determine the value for the X for "Showing X of Y"
{
int currentTotal = 0;
if ((Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage) < Model.PagingInfo.TotalItems) {
//the current max item we are displaying is less than the total number of policies
//display the current max item index\
currentTotal = Model.PagingInfo.CurrentPage * Model.PagingInfo.ResultsPerPage;
} else {
//the current is greater than the total number of policies
//display the total number of policies
currentTotal = Model.PagingInfo.TotalItems;
}
if (Model.PagingInfo.TotalPages == 0 || Model.PagingInfo.CurrentPage == Model.PagingInfo.TotalPages)
{
#<li>
<h3>#Model.PagingInfo.LinkTextShowingAll</h3>
<p><strong>Showing #currentTotal Of #Model.PagingInfo.TotalItems</strong></p>
</li>
} else {
#<li id="GetMore">
<a href="#" id="lnkGetMore">
<h3>#Model.PagingInfo.LinkTextShowMore</h3>
<p><strong>Showing #(currentTotal) Of #Model.PagingInfo.TotalItems</strong></p>
</a>
</li>
#<script type="text/javascript" lang="javascript">
$('#lnkGetMore').click(function () {
$.ajax({
url: "#Model.PagingInfo.UrlGetMore",
success: function (data) {
$('#ProducerList li:last').remove();
$('#ProducerList').append(data);
$('#ProducerList').listview('refresh');
}
});
return false;
});
</script>
}
}
Now, the javascript at the end is specifically for a UI that uses ul's and li's, but can easily be customized for your needs.
The UrlGetMore property is set on the back end when the model is passed to the view. I am sure there is a more elegant way of doing this. Here is the code I used:
//build paging url used by the jQuery Ajax function
view.PagingInfo.UrlGetMore == Url.RouteUrl("RouteItemList", new { page = view.PagingInfo.CurrentPage + 1 })
And finally, here is the action that handles both the initial View and the subsequent Partial View (ajax call)
public ActionResult List(UserModel user, ViewModel view, int page = 1)
{
IQueryable<model> models = this.RetrieveModels(user, view);
if ((models != null) && models.Count > 0) {
view.PagingInfo.CurrentPage = page;
view.PagingInfo.ResultsPerPage = user.Preferences.ResultsPerPage;
view.PagingInfo.TotalItems = models.Count;
view.items = models.Skip((page - 1) * user.Preferences.ResultsPerPage).Take(user.Preferences.ResultsPerPage).ToList();
//build paging url used by the jQuery Ajax function
view.PagingInfo.UrlGetMore = Url.RouteUrl("RouteList", new { page = view.PagingInfo.CurrentPage + 1 });
}
if (page == 1) {
return View(view);
} else {
return PartialView("ListPartial", view);
}
}
HTH.
You could create simple HtmlHelper simillar to this:
public static class HtmlPaginHelper
{
public static MvcHtmlString PagerNoLastPage(this AjaxHelper ajaxHelper,
int page,
int pageSize,
bool isLastPage,
Func<int, string> pageUrl,
Func<int, AjaxOptions> pageAjaxOptions)
{
var result = new StringBuilder();
var firstPageAnchor = new TagBuilder("a");
firstPageAnchor.SetInnerText("<<");
var prevPageAnchor = new TagBuilder("a");
prevPageAnchor.SetInnerText("<");
var nextPageAnchor = new TagBuilder("a");
nextPageAnchor.SetInnerText(">");
var currentPageText = new TagBuilder("span");
currentPageText.SetInnerText(string.Format("Page: {0}", page));
if (page > 1)
{
firstPageAnchor.MergeAttribute("href", pageUrl(1));
firstPageAnchor.MergeAttributes(pageAjaxOptions(1).ToUnobtrusiveHtmlAttributes());
prevPageAnchor.MergeAttribute("href", pageUrl(page - 1));
prevPageAnchor.MergeAttributes(pageAjaxOptions(page - 1).ToUnobtrusiveHtmlAttributes());
}
if (!isLastPage)
{
nextPageAnchor.MergeAttribute("href", pageUrl(page + 1));
nextPageAnchor.MergeAttributes(pageAjaxOptions(page + 1).ToUnobtrusiveHtmlAttributes());
}
result.Append(firstPageAnchor);
result.Append(prevPageAnchor);
result.Append(currentPageText);
result.Append(nextPageAnchor);
return MvcHtmlString.Create(result.ToString());
}
}
... and then use it in your Razor view:
grid results go here...
#Ajax.PagerNoLastPage(Model.Query.Page,
Model.Query.PageSize,
Model.Data.IsLastPage,
i => Url.Action("Index", RouteValues(i)),
i => new AjaxOptions
{
UpdateTargetId = "content",
InsertionMode = InsertionMode.Replace,
HttpMethod = "GET",
Url = Url.Action("Grid", RouteValues(i))
})
where RouteValues(i) is defined for example like this:
#functions {
private object PageRouteValues(int i)
{
return new
{
payId = Model.Query.PayId,
clientCode = Model.Query.ClientCode,
fromDate = Model.Query.FromDate,
tillDate = Model.Query.TillDate,
payNum = Model.Query.PayId,
checkNum = Model.Query.CheckNum,
payType = Model.Query.PayType,
payStatus = Model.Query.PayStatus,
page = i,
pageSize = Model.Query.PageSize
};
}
}
Is that even possible using an mvc helper or mvc.pagedlist?
Yes, but of course you have to coordinate the client-side requests with server-side actions to handle the actual data paging. In that sense, it's not as simple as as WebForms, but it's still possible.
Here's an example of using PagedList to render each returned item in its own table, separated by horizontal rules. You should easily be able to modify the HTML in the example to produce any rendering you want.

Resources