Posting two and more lists in asp.net mvc - c#

I'm using asp.net mvc 2 and i found this behavour which i can't understand.I have following view:
<% using (Html.BeginForm("Index", "BlackListGrabber", FormMethod.Post) )
{
<%= Html.DropDownListFor(m => m.selectedArea, new SelectList(Model.areaList, "value", "text")) %>
<% if (Model.districtList != null) { %>
<%= Html.DropDownListFor(m => m.selectedDistrict, new SelectList(Model.districtList, "value", "text")) %>
<% } %>
<% if (Model.townList!= null) { %>
<%= Html.DropDownListFor(m => m.selectedTown, new SelectList(Model.townList, "value", "text")) %>
<% } %>
<input type="submit" value="post" />
<% } %>
and a controller's method like this:
[HttpPost]
public ActionResult Index(BlackListGrabberModel postedModel)
{
BlackListGrabberModel model = new BlackListGrabberModel(postedModel);
return View(model);
}
And, last but not least, my model:
BlackListGrabberModel(BlackListGrabberModel model)
{
if (string.IsNullOrEmpty(model.selectedArea))
{
areaList = GetRegions();
}
else if (string.IsNullOrEmpty(model.selectedDistrict))
{
areaList = model.areaList;
districtList = GetRegions(model.selectedArea);
}
else if (string.IsNullOrEmpty(model.selectedTown))
{
areaList = model.areaList;
districtList = model.districList;
districtList = GetRegions(model.selectedDistrict);
}
}
Idea is that then i load page, i see list of all possible areas.(And i see it - it's my first dropdownlistfor) When i select area, after clicking "post" button, i see list of all districts, they loaded from external source and this part works fine.
So i select district from list, and click "post". After thar i see list of all towns located in selected district, but districtList disappears. Then i traced it in my controller, i found that property postedModel.districtList is null. But postedModel.areaList is fine! Does that mean that i can post only one SelectList, or i'm missing something? Can somebody please give me any help?
P.S. Properties "selectedArea", "selectedDistrict", "selectedTown" are posted as expected.
EDIT. Thanks to everybody, i missed some important things, and you gave me direction to them.
My problem appeared to be areaList. It was filled by default constructor. I forgot about that, so then i saw postedModel.areaList filled, i thought it was magically posted by asp.net mvc mechanisms, and complained that all other lists are not filled because of some strange glithces.

You will have to repopulate your list properties in your model for every request.
The won't get posted back automatically. Just the selected value is posted back and bound to the property in your model (i.e. selectedArea is bound but not areaList).

The lists should not post, only the values of the select elements in your html form will. If you need to hold onto the list values, you might try placing them in TempData in you GET for Index, which will keep them for the next request.

Related

PagedList.MVC - Page buttons(links) don't work as expected

I wanted to implement a simple pagination, and PagedList.MVC NuGet package sounded like the best solution for me. HOWEVER, when I click on generated buttons to go to 2nd, 3rd, etc. page, 1st one remains active, and all that happens is refresh of the first page, but I obviously want it to navigate to the expected page...
I followed these two tutorials to see if I've done everything right:
Github
Microsoft
My controller:
public ActionResult Index(int? pageNumber)
{
var modelList = _employeeService.GetEmployeeViewToPagedList(pageNumber);
return View(modelList);
}
The service method that gets called (I know that "ToPagedList()" is usually called from the controller, but the current state is a result of trying everything, and the fact that I get "DbContext disposed" error if I modify to return something like "View(modelList.ToPagedList(pageNumber, pageSize))" from the controller):
public IPagedList<EmployeeView> GetEmployeeViewToPagedList(int? pageNumber)
{
using (var _unitOfWork = UnitOfWork.GetUnitOfWork())
{
var list = (IQueryable<EmployeeView>)_unitOfWork.context.EmployeeViews.OrderByDescending(x => x.Id);
return list.ToPagedList((pageNumber ?? 1), 10);
}
}
My view:
#model PagedList.IPagedList<Company.DAL.Views.EmployeeView>
#using PagedList.Mvc;
<link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
#{
ViewBag.Title = "Index";
}
<h2>List of all employees</h2>
<p>
#Html.ActionLink("Add new employee", "AddNewEmployee")
</p>
#if (Model != null && Model.Count() > 0)
{
<table class="table">
... all needed <tr>'s, <th>'s, <td>'s ...
</table>
<br/>
#Html.PagedListPager(Model, page => Url.Action("Index", new { page, pageSize =
Model.PageSize }))
}
I am trying to figure this out for days now, and the closest I got was this question, but I am not sure where to find that JS function, so I could try that as well.
EDIT:
Generated HTML:
<div class="pagination-container">
<ul class="pagination">
<li class="active"><a>1</a></li>
<li>2</li>
<li>3</li>
<li class="PagedList-skipToNext">»</li>
</ul>
</div>
I decided to post an answer here, since I solved the problem, and somebody else might find this useful.
So, in the controller, my Index method looks like this:
public ActionResult Index(int? pageNumber)
{
//some logic
}
As you can see, it accepts an int variable named pageNumber as a parameter.
But then there's this on my view:
#Html.PagedListPager(Model, page => Url.Action("Index", new { page, pageSize = Model.PageSize }))
SO, here I am passing a variable named page to my Index method.
That's the mistake! Variable in the method parameter list has to be named page as well.

Best way to handle add/view/delete on one page

What I want to do
I am very new to MVC.
I'm trying to create a page that allows users to perform the following actions on the same page:
View the list (table)
Add a new item (Filling the form and clicking the Add button should update the table)
Delete an item from the list (Clicking the Delete button in a row should update the table)
A simple example looks like this but I actually have two lists on one page (Fees and Costs):
Question
What would be the best way to achieve this?
Should I go with Dylan Beattie's method posted here which would look something like this?
public ActionResult MyAction(string submitButton, MyViewModel form)
{
switch (submitButton)
{
case "AddFee":
return (AddFee(form));
case "AddCost":
return (AddCost(form));
case "RemoveFee":
return (RemoveFee(form));
case "RemoveCost":
return (RemoveCost(form));
}
}
public ActionResult AddFee(MyViewModel form)
{
Fee newFee = ....; // Get entered data from `form`
_repository.InsertFee(newFee);
return View("Create"); //Back to the original page
}
Or is there any other recommended methods to handle this such as using JavaScript?
You could create the table as a partial view and re render this via ajax.
Wrap the partial view in a div and Wrap the form in #using (Ajax.BeginForm(.... and target the wrapper div. Your controller action that is targeted by the ajax request will need to return a partial view.
Here is a simple example
public class HomeController : Controller
{
public ActionResult Index()
{
MYvm vm = new MYvm() { id = 1, name = "This is my View Model" };
return View(vm);
}
public ActionResult DA(MYvm vm)
{
vm.name = "CHANGED";
return PartialView("Part", vm);
}
View:
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "Home Page";
}
#using (Ajax.BeginForm("DA", "Home", new AjaxOptions() { UpdateTargetId = "cont", HttpMethod = "Get" }))
{
<div>
Id: #Html.EditorFor(model => model.id)
</div>
<div>
Name: #Html.EditorFor(model => model.name)
</div>
<input type="submit" value="SubmitForm" />
}
<div id="cont">
#{Html.RenderPartial("part", Model);}
</div>
Partial View
#model MvcApplication1.Controllers.HomeController.MYvm
#{
ViewBag.Title = "part";
}
<h2>part</h2>
#Model.name
Should I go with [previous SO answer]
No. That answer was for a different scenario where the question had a form with two submit buttons that wanted to do two different actions (and wasn't even the accepted answer to that question).
Your sample screenshot indicates that some javascript/jquery and ajax would solve the issue cleanly.
As you're new to MVC, try to keep it relatively simple. Break up the page into separate parts:
the containing page
the edit form
the list with remove
the edit/list work independently and should be written in a way that they could be put on any other page - the page is just there to contain them and doesn't do much else (obviously your real page will contain more, but add those parts as separate components as well).
1 Create actions for your list and edit forms that return partialviews - just the parts that are needed for that view (self-contained)
controller:
[HttpGet]
public ActionResult AddCost()
{
var model = new Cost();
return PartialView(model);
}
[HttpPost]
public void AddCost(Cost model)
{
if (ModelState.IsValid) {
db.SaveCost(model);...
}
}
form Views/Home/AddCost.cshtml:
#using (Ajax.BeginForm(...
{
<div class='editor-label'>#Html.LabelFor(model=>model.Description)</div>
...etc...
}
I'll leave you to set the Ajax.BeginForm properties. But make sure the on-success calls reloadCostList() (see below)
controller
public ActionResult CostList()
{
var model = db.loadCosts(); ...
return PartialView(model);
}
list, Views/Home/CostList.cshtml
#model IEnumerable<ViewModels.Cost>
<table>
<thead>
<tr>
<th>Cost Description</th>
...
<tbody>
#foreach (var cost in Model.Costs)
{
<tr data-id='#cost.Id'>
<td>#Html.DisplayFor(x=>cost.Description)</td>
...
<td><a href='#' class='remove-button'>Remove</a></td>
}
...
2 Create an action + view for the main page with placeholder for the form and calls the list partial-action, eg:
<div id="body">
<div id="formWrapper">
#Html.Action("AddCost")
</div>
<div id="listWrapper">
#Html.Action("ListView")
</div>
</div>
if you already load the data for the page, you can pass it directly to the partial, but there's no need:
#Html.Partial("ListView", Model.Costs)
this allows you to refresh the list via an ajax call, something like:
function reloadCostList() {
$(".listWrapper").load("Home/CostList");
}
(ideally, $.ajax and add some fancy UI to indicate loading)
3 Add a remove action to your controller
[HttpPost]
public void RemoveCost(int id)
{
}
4 Wire up the Remove link
$(function() {
$(".remove-button").click(function() {
var id = $(this).closest("tr").attr("id");
$.post("/Home/RemoveCost/" + id, null, function() {
$(".listWrapper").load("Home/CostList");
// or reloadCostList(); from above
// or:
//$(".listWrapper tr[id=" + id + "]").hide();
});
});
}
rather than re-load the entire list, you could just remove the row (add some fancy UI like fade-out...)

Hook javascript to dropdownlist change

¡Hola!
My current task is to have a page where, with a dropdownlist, a user can select a deck title. Once a title is selected, the page should postback with details on that deck.
Here's what I've got at the moment:
#model IEnumerable<SCATChartsMVC.Models.Charts_DeckList>
#{
ViewBag.Title = "Index";
if (IsPost) { ViewBag.Title = "We posted back!"; }
}
<h2>Index</h2>
#{ var list = ViewData.Model.Select(cl => new SelectListItem
{
Value = cl.RecNum.ToString(),
Text = cl.DeckTitle.ToString()
});
}
#using (Html.BeginForm("Details", "Charts_DeckList", FormMethod.Post))
{
#Html.DropDownList("deckTitles", list, "---------select---------")
<input type="submit" name="submit" value="Submit" />
#Html.ActionLink("Details", "Details", "Charts_DeckList", new { id = list.ElementAt(4).Text }, "")
}
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script>
$("deckTitles").change(function () {
if ($("#deckTitles").val() != "") {
var test = {};
test.url = "/Charts_DeckList/Details";
test.type = "POST";
test.data = JSON.stringify($('#deckTitles').val());
test.datatype = "json";
test.contentType = "application/json";
test.success = true;
test.error = function () { alert("Error!"); };
$.ajax(test);
}
})
</script>
The input tag and ActionLink under Html.BeginForm were for my own testing purposes; the ActionLink works correctly if I specify the element. I'm hoping to be able to pass something similar back whenever a user clicks a selection, as opposed to whenever they hit the "details" button.
The submit input tag does not work. It does route properly to Charts_DeckList/Details, but the parameter in the action is always null.
I'm just getting into the whole MVC/Web rigamarole, so there's a lot I don't know that I'm not even aware I don't know. While I've seen a number of different resources on the internet suggesting different things, much of the web development jargon is lost on me at this point in time, and much of the way these things work under the hood is lost on me since VS seems to put together so much of it automagically.
Any pointers would be appreciated. Thank you.
barrick's suggestion below is correct!
I also had to move the script tags up into the BeginForm brackets, heads up.
You're not setting the ID of the DropDownList there, the first argument sets the name attribute of the dropdown (used to identify the value in the POST variable collection on server postback) - you'll need to add another argument to set the ID:
#Html.DropDownList("deckTitles", list, "---------select---------", new { #id = "deckTitles" });
You can then pick up the selected value in the jQuery as follows:
$("#deckTitles option:selected").val();

Generating HTML from server-side block in ASP.NET MVC

This is a very newbie kind of ASP.NET question: I simply don't know and can't work out the correct syntax to use.
In my view I want to generate an action link if a certain condition is true on my model. I know how to generate a link using this syntax:
<%: Html.ActionLink("Do Something", "DoSomething", new { id = Model.ID }) %>
But for some reason that syntax doesn't work in this code:
<%
if (Model.CanDoSomething)
Html.ActionLink("Do Something", "DoSomething", new { id = Model.ID });
%>
I really am a newbie to ASP.NET, so I don't even know what the semantic name is for the different syntaxes <% and <%:; all I can tell is that <% is to void as <%: is to string. And clearly executing a line of code that just returns a string (Html.ActionLink()) is not going to have any effect. But what, pray what is the correct method to make my page render the action link?
It's a great pity I can't Google on "<%"! Any links or explanations of this subject will also be much appreciated.
This will do the trick
<% if (Model.CanDoSomething) { %>
<%: Html.ActionLink("Do Something", "DoSomething", new { id = Model.ID }) %>
<% } %>
<%: writes to the output buffer but encodes the string. You could also use <%= for unencoded output because ActionLink returns an encoded MvcHtmlString.
EDIT: This may also work
<%
if (Model.CanDoSomething)
Response.Write(Html.ActionLink("Do Something", "DoSomething", new { id = Model.ID }));
%>
<% - this by itself has no output. You would include code with no output in this block such as an if statement. If you want output - you must use it in conjunction with <%=
The difference with the : is that
<%: means it will output to the response stream, not = required however the : means the output will be htmlencoded.
<%:"sometest&text" %> //will emit "sometesttext" on the page.. htmlencoded.
<%="sometest&text" %> //will give you the same result without the '&' htmlencoded
<% SomeFunction() %> //will just run that function - there is no output
//you want
<%if (Model.CanDoSomething){%>
<%:Html.ActionLink("Do Something", "DoSomething", new { id = Model.ID })%>
<%}%>

C# How to set the autopostback property when using asp.net mvc?

I am using asp.net MVC framework. On my page i have a dropdwonbox and when an option is clicked i want to go to another page. But i can't find how/where to set the autopostback property to true. This is the code i'm using:
Aspx:
<%= Html.DropDownList("qchap", new SelectList( (IEnumerable)ViewData["qchap"], "Id", "Title" )) %>
Controller:
public ActionResult Index(int id)
{
Chapter c = new Chapter();
ViewData["qchap"] = c.GetAllChaptersByManual(id);
return View();
}
What do i have to do to use the autopostback functionality?
You can use the onchange client event:
<%= Html.DropDownList("qchap",
new SelectList( (IEnumerable)ViewData["qchap"], "Id", "Title" ),
new { onchange = "this.form.submit();" }) %>
It seems the DropDownList helper method doesn't support this.
Maybe using it within a form and a custom custom html attribute to submit the form do it.
I believe too that you may want to adjust your postback to the formsCollection
postback public ActionResult Index(FormsCollection myform)
(I'm not on my home pc where MVC is installed, so I can't verify the syntax here)
I solve using this code.
Function Index(ByVal collectionField As FormCollection) As ActionResult
Dim industryCategoryID As Long = collectionField.Item("ddlIndustry")
If industryCategoryID = 0 Then
Me.ViewData("IndustryList") = GlobalController.GetIndustryList
Return View(_service.ListCompanies())
Else
Me.ViewData("IndustryList") = GlobalController.GetIndustryList
Return View(_service.ListCompanies(industryCategoryID))
End If
End Function
That's for the ActionResult function
And Then for the View
<p>
<% Using Html.BeginForm()%>
<%=Html.DropDownList("ddlIndustry", New SelectList(CType(ViewData("IndustryList"), IEnumerable), "ID", "Name"), "--Choose industry--", New With {.onchange = "this.form.submit()"})%>
<% End Using %>
</p>
I hope it helps. I f you would like more complete codes please feel good to email me at boylevantz#gmail.com

Categories

Resources