I have a form, with a partial view inside it to render several child controls.
Main view :
#model Test_mvc.Models.Entity.Question
#{
ViewBag.Title = "Edit";
Layout = "~/Areas/Admin/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#*snip*#
<fieldset>
<legend>Profils</legend>
#Html.Action("CheckboxList", "Profil", new { id_question = Model.id })
</fieldset>
<p>
<input type="submit" value="Enregistrer" />
</p>
}
Profil controller (for the partial view) :
[ChildActionOnly]
public ActionResult CheckboxList(int id_question)
{
var profils = db.Profil.Include("Profil_Question")
.OrderBy(p => p.nom).ToList();
ViewBag.id_question = id_question;
return PartialView(profils);
}
Profil.CheckBoxList view :
#model List<Test_mvc.Models.Entity.Profil>
#foreach (var p in Model)
{
<input type="checkbox" name="profil_#(p.id)"
#if (p.Profil_Question.Where(pc => pc.id_question == ViewBag.id_question).Any())
{
#:checked="checked"
} />
#Html.Label("profil_" + p.id, p.nom)
<br />
}
(I don't want to use #Html.CheckBox because I don't like being sent "true,false" when the checkbox is checked).
Today, if I want to get the checkboxes that have been checked, I do this, but I think it's awful :
Question controller (for the main view) :
[HttpPost]
public ActionResult Edit(Question question)
{
if (ModelState.IsValid)
{
db.Question.Attach(question);
db.ObjectStateManager.ChangeObjectState(question, EntityState.Modified);
db.SaveChanges();
// this is what I want to change :
foreach (string r in Request.Form)
{
if (r.StartsWith("profil_") && (Request.Form[r] == "true" || Request.Form[r] == "on")) {
var p_q = new Models.Entity.Profil_Question();
p_q.id_profil = int.Parse(r.Replace("profil_", ""));
p_q.id_question = question.id;
db.AddToProfil_Question(p_q);
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(question);
}
How would you replace the "foreach" in the last code section ?
Thanks
The first thing I would try would be to give all my checkboxes the same name and put the #id as the value of the box:
#foreach (var p in Model) {
<input type="checkbox" name="profil_checkbox" value="#p.id"
#if (p.Profil_Question.Where(pc => pc.id_question == ViewBag.id_question).Any())
{
#:checked="checked"
} />
#Html.Label("profil_" + p.id, p.nom) <br /> }
Then rather than searching for profil_#id I should get an array of results for profile_checkbox which is much easier to work with. I don't recall exactly how MVC3 processes this, so I can't guarantee what exactly you'll get in the postback, but that should be easy enough to check during debugging.
Related
I have Get and Post partial Action. Get take me a list of image which I have in ma app.
[HttpGet]
public PartialViewResult ViewImageFileList()
{
IEnumerable<string> allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PartialView(allImages);
}
Post delete image which I extra.
[HttpPost]
public PartialViewResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
return PartialView();
}
My .chhtml of my partial view
#model IEnumerable<string>
<div class="name-block-style">
Логотипы которые имеются
</div>
<div id=team-logo-wrapper-images>
<ul>
#foreach (var fullPath in Model)
{
var fileName = Path.GetFileName(fullPath);
<li>
<div class="box-name-image">
<p class="image-name-type">#fileName</p>
<img src="#Url.Content(string.Format("~/Images/NBAlogoImg/{0}", fileName))"
class="logo-images" alt="Логотип команды"
title="Логотип команды" />
</div>
</li>
}
</ul>
<div id="delete-image-form" class="form-group">
#using (Ajax.BeginForm(
"ViewImageFileList",
"Team",
new AjaxOptions() { HttpMethod = "POST", OnComplete = "reloadPage()" }))
{
<label>Введите имя с указание типа изображения</label>
<input type="text" class="form-group" name="imageNameType" id="imageNameType" />
<input type="submit" value="Удалить" class="btn btn-primary" />
}
</div>
<script>
function reloadPage() {
location.reload();
}
</script>
My problem is Null references when I write the deleting image and submit it(i do it by ajax). I have this error Null reference but when I click to continue, the image deleted and my script to reload page work.
I want to understand why I take the null and how I can fix it, because it stops my app always when I delete an image.
The problem is that when you POST after you delete the image you don't populate the model of the partial view, as you do correctly in ViewImageFileList. This has a result when the View Engine try to build the view that you would send after the POST to the client, to get a null reference exception when try to perform the foreach on a null reference.
That being said, the thing you need is to pass to the PartialView all the images. So just add before the return statement in the action method you POST this:
var allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PatialView(allImages);
When you browsing images you return view with model passed
return PartialView(allImages); //allImages is a model
But when you deleting images you return view without any model
return PartialView(); //need to pass a model
So after deleting you would like to redirect to ViewImageFileList to browse
all images
[HttpPost]
public RedirectToRouteResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
return RedirectToAction("ViewImageFileList");
}
or retrieve images in delete action once again and pass the list to view
[HttpPost]
public PartialViewResult ViewImageFileList(string imageNameType)
{
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), imageNameType);
if (System.IO.File.Exists(fileToDeletePath))
{
fileOperations.Delete(fileToDeletePath);
}
IEnumerable<string> allImages = Directory.EnumerateFiles(Server.MapPath("~/Images/NBAlogoImg/"));
return PartialView(allImages);
}
I wanted the pagination to happen asynchronously but the complete page reloads on selecting a page. Can anyone please tell me how to make the pagination happen asynchronously?
Here's the code.
#Html.PagedListPager(listData, page => Url.Action("ViewOnMap", new { search, Beds, Baths, minprize, maxprize, page, IsSearch, SearchFilterBy }),PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(new AjaxOptions() { HttpMethod="GET", UpdateTargetId= "DivPropertyOnMap" }))
Why don't you use this. You could use something like this.
https://www.nuget.org/packages/PagedList
public IPagedList<listData> getdata(int? Page, int? pageIndex)
{
...
return data.OrderByDescending(x => x.OnSave).ToPagedList(Page ?? 1, pageIndex ?? 10);
}
Controller
public ActionResult List(datamodel model)
{
var dataModel = new getdata(model.Page, 10);
return View("_dataList", dataModel );
}
Razor list.cshtml
<div id="listdata">
#Html.Partial("_List", #Model)
</div>
_list_cshtml
#model youdatemodel
#using PagedList;
#using PagedList.Mvc;
<ul>
#foreach (var item in Model.dataModel)
{
<li>
#item.dataitem;
</il>
}
</ul>
#Html.PagedListPager(Model.modeldata, page => Url.Action("List", new RouteValueDictionary
{
{
"Page",page
}
}), PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(new AjaxOptions { UpdateTargetId = "listdata" }))
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...)
I have a partialview _Psite which contains two dropdownlist and a text box, second one is based one first as Jsonresult (cascading dropdowns). So now suppose if customer select values in first dropdownlist, second one will load based on jquery and json.Then when he enter wrong values in text box validation fails(Session["username"] == null) it will display the same partial view after post in order to reenter .The problem now i am facing is the two dropdownlist is resetting in to default values.I have googled but couldn't find a solution
Following is view of _Psite
#using (Ajax.BeginForm("_firstGridAll", "mnis", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "PsitegridContent" }))
{
<div style="float: left">
#Html.DropDownList("REGION_CODE", (SelectList)ViewBag.Categories, "Select region code")
#Html.ValidationMessageFor(m => m.REGION_CODE)
</div>
<div class="tested">
<select id="GEO_ZONE_CODE" name="GEO_ZONE_CODE"></select>
</div>
<div class="tested">
#Html.TextBoxFor(m => m.PSITE_ID)
#Html.ValidationMessageFor(m => m.PSITE_ID)
</div>
<div class="Testedagain">
<input type="submit" value="Search" />
</div>
}
Controller is
public ActionResult _Psite()
{
if (TempData["values"].ToString() == "value persists")
{
ViewBag.change = true;
// ViewBag.Categories = TempData["EnterUniqueKeyHere"];
// return PartialView("_failValidation");
}
var categories = db1.MN_PSITE.Select(c => new
{
REGION_CODE = c.REGION_CODE,
CategoryName = c.REGION_CODE
}).Distinct().ToList();
ViewBag.Categories = new SelectList(categories, "REGION_CODE", "CategoryName");
ViewBag.error = false;
ViewBag.change = false;
return PartialView();
}
and the controller for validating data is following
[HttpPost]
public ActionResult _firstGridAll(string REGION_CODE, string GEO_ZONE_CODE, string PSITE_ID)
{
if (ModelState.IsValid == true)
{
Session["username"] = null;
var items = db1.MN_PSITE.Where(x => x.REGION_CODE == REGION_CODE).Where(y => y.GEO_ZONE_CODE == GEO_ZONE_CODE).Where(z => z.PSITE_ID == PSITE_ID);
//db1.MN_PSITE.Where(x => x.REGION_CODE == Region).Where(y => y.GEO_ZONE_CODE == GeoZONE).Where(z => z.PSITE_ID == Psiteid);
foreach (var it in items)
{
Session["username"] = it.PSITE_SLNO.ToString();
return PartialView(items.ToList());
}
if (Session["username"] == null) //validation fails
{
TempData["error"] = "value doesnot exisit,please renter the details";
return RedirectToAction("_Psite");
}
}
//count = 0;
return PartialView(db1.MN_PSITE.ToList());
}
UPDATE
i am using Entityframework generated classes as model no view viewmode ,do here 'db' is an instance of entity class
If you were posting a view model into your action instead of individual parameters, then you would be able to simply pass that model back out in your partial at the end of the action.
It will only be a small class with a few properties, so will take a minute to create. Do it. It will give you what you want.
I have the following idea that i am trying to implement
#foreach (var item in Model)
{
<div>User: #item.Name<br />
Scores: #item.scores<br />
#Html.TextBox("lastvisit");
#Html.ActionLink("Update item", "updateMyItem", new { name = item.Name, lastvisit=????? })
</div>
}
I have seen this SO question Pass text in query string, but that is not what i want..
so my question is ..
in the above code how can I replace the (?????) with the value of the textbox(lastvisit)
and send the value as a querysting in the URL of the action link ??
Notice that I opted not to use a webform for my own reason and I know how to do it with webform.submit(), but my main concern is how to extract the value of #HTMLhelper.textbox()..
:)
Something like this might help. For this to work you need to render unique IDS for the links and textboxes.
Here is an example
Action method with a simple model
public ActionResult Index(int? id)
{
List<MyModel> mod = new List<MyModel>() {
new MyModel { SelectedValue = 1 } ,
new MyModel {SelectedValue = 2},
new MyModel {SelectedValue = 3}
};
return View(mod);
}
And this is the view with the script.
#model List<MVC3Stack.Models.MyModel>
#{
ViewBag.Title = "Home Page";
var i = 1;
}
<h2>#ViewBag.Message</h2>
<script type="text/javascript">
$(document).ready(function () {
var lastVisits = $("input[id*='lastVisit']");
$(lastVisits).each(function () {
var i = this.id.substring(this.id.length - 1);
var link = $("[id='testLink" + i + "']");
if (link) {
var _href = $(link).attr("href");
$(link).attr("href", _href + "&lastvisit=" + $(this).val());
}
});
});
</script>
#foreach (var item in Model)
{
#Html.TextBox("lastVisit" + i, item.SelectedValue )
#Html.ActionLink("TestLink", "Index", "Home", new { id = "testLink" + i });
<br />
i++;
}
<input type="button" value="GetFile" id="getFile" />
here is a snapshot with the changed link
Hope this helps.
EDIT
My bad. Here is the update javascript which can do the trick.
$(document).ready(function () {
var lastVisits = $("input[id*='lastVisit']");
$(lastVisits).each(function () {
$(this).change(function () {
var i = this.id.substring(this.id.length - 1);
var link = $("[id='testLink" + i + "']");
if (link) {
var _href = $(link).attr("href");
$(link).attr("href", _href + "?lastvisit=" + $(this).val());
}
});
});
});
Ok Nilesh I will answer my own question.. but I will cheat from your solution lol cuz it is inspiring .. thanx in advance
<script type="text/javascript">
$(document).ready(function () {
var myMainPath = "updateMyItem";
$("a").each(function(){
var name =$(this).parent("div").child("#itemName").val();
var visit = $(this).parent("div").child("#lastvisit").val();
$(this).attr('href', myMainPath +'?name=' + name + '&lastVisit='+ visit);
});
});
</script>
#foreach (var item in Model)
{
<div>User: <span id="itemName">#item.Name</span><br />
Scores: #item.scores<br />
#Html.TextBox("lastvisit", new { id="lastvisit"});
Update item
</div>
}
you see it can be done by javascript , but i was mistaken to think that you can manipulate it via Razor on the server ..
I know this post is old, but i just started learning MVC thanks to the asp.net/mvc/ website and i faced a similar problem during the tutorial. My Index action expects 2 parameters which define sorting and filtering (through the macthing of a substring) of a set of record displayed in the view. My problem is that i can't sort a filtered subset, since the view is called but no parameter for filtering is passed once i activate the sorting clicking on the link of the header.
#* Index.cshtml *#
#using (Html.BeginForm())
{
<p>
Find by name: #Html.TextBox("SearchString")
<input type="submit" value="Search" />
</p>
}
. . .
<!-- header -->
<table><tr><th>
#Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm })
</th>
. . .
//controller.cs
public ActionResult Index(string sortOrder, string searchString){...}
I thought i needed to access the TextBox, but apparently i just need to use the provided ViewBag object as already seen in this example!
#* Index.cshtml *#
#using (Html.BeginForm())
{
<p>
Find by name: #Html.TextBox("SearchString")
<input type="submit" value="Search" />
</p>
}
. . .
<!-- header -->
<table><tr><th>
#Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm, searchString = ViewBag.SearchString })
</th>
. . .
//controller.cs
public ActionResult Index(string sortOrder, string searchString)
{
ViewBag.SearchString = searchString;
. . .
}
Maybe a similar behaviour could have been used for solving the problem that originated this post, i don't know.