Partial View with parametrized prefix for controls names - asp.net-mvc

I have a BarEditor.ascx, that can be called from diffent places.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyApp.Models.Bar>" %>
<%= Html.TextBox("a") %>
...
Now consider I need to edit two objects on one page Edit.aspx
<form action="update">
<div>
<% Html.RenderPartial("BarEditor", ViewData["bar"]); %>
</div>
<div>
<% Html.RenderPartial("BarEditor", ViewData["baz"]); %>
</div>
<input type="submit" value="Submit" />
</form>
This submits:
a=1&a=2
I need it to be:
bar.a=1&baz.a=2
So we can process it with
public ActionResult Update(Bar bar, Bar baz)
{
...
}
What is a best way to write reusable BarEditor.ascx that can generate prefixes for controls names?

just create a ViewModel class for your BarEditor and make it strongly typed to this new class
e.g.
namespace ViewModel {
public class BarEditor {
string Prefix { get; set; }
Models.Bar Bar { get; set; }
}
}
now you create your textbox in BarEditor.ascx like this
<%= Html.TextBox(Model.Prefix + ".a") %>
and in your view you include the BarEditor like that
<form action="update">
<div>
<% Html.RenderPartial("BarEditor", new ViewModel.BarEditor { Prefix = "Bar", Bar = ViewData["bar"]}); %>
</div>
<div>
<% Html.RenderPartial("BarEditor", new ViewModel.BarEditor { Prefix = "Baz", Bar = ViewData["baz"]}); %>
</div>
<input type="submit" value="Submit" />
</form>
hth

I would pass a string ("baz" or "bar", etc) with my ViewData when calling the user control. Have the html.textbox get its name from the text passed and its value from the value passed.

Why not create a model for the view? Your view would then need to be a strongly typed view using the data class FormView.
public class FormView
{
string Bar {get; set;}
string Baz {get; set;}
}
Then in your view you can use
<form action="update">
<div>
<% Html.RenderPartial("BarEditor", Model.Bar); %>
</div>
<div>
<% Html.RenderPartial("BarEditor", Model.Baz); %>
</div>
<input type="submit" value="Submit" />
</form>
Your controller becomes
public ActionResult Update(FormView MyForm)
{
... = MyForm.Bar;
... = MyForm.Baz;
}

You should to learn about Model Mapping in ASP.Net MVC. Everything in the asp.net mvc page will be rendered to html control therefore don't distinguish between controls in
<% Html.RenderPartial("BarEditor", ViewData["bar"]); %>
and
<% Html.RenderPartial("BarEditor", ViewData["baz"]); %>

Related

Return a generic list to the MVC Controller

I have a class that looks like this;
public class item
{
public string Tradingname { get; set; }
}
I have a Partial View that inherits from the "item" class;
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<item>" %>
<%= Html.TextBoxFor(x => x.Tradingname) %>
In my view I create a number of them. Let's Say 2;
<% using (Html.BeginForm())
{ %>
<% Html.RenderPartial("TradingName", new Binding.Models.item()); %><br />
<% Html.RenderPartial("TradingName", new Binding.Models.item()); %><br />
<input type="submit" />
<%} %>
Then in my controller I was hoping to be able to write this;
[HttpPost]
public ActionResult Index(List<item> items)
or
[HttpPost]
public ActionResult Index([Bind(Prefix = "Tradingname")]List<item> items)
But I can't seem to get any data back from my partial views into my List. Anyone know how I can get a variable list of data back from a variable set of partialViews?
I would recommend you using editor templates:
Controller:
public class HomeController: Controller
{
public ActionResult Index()
{
List<item> model = ...
return View(model);
}
[HttpPost]
public ActionResult Index(List<item> items)
{
...
}
}
and in the view:
<% using (Html.BeginForm()) { %>
<%= Html.EditorForModel();
<input type="submit" />
<% } %>
and finally simply move the partial in ~/Views/Home/EditorTemplates/item.ascx (name and location are important):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<item>" %>
<%= Html.TextBoxFor(x => x.Tradingname) %><br/>

Asp.net mvc 2 templates without prefix

Given following view model:
class DetailsViewModel
{
public HeaderViewModel Header {get;set;}
public FooterViewModel Footer {get;set;}
}
I'm using editor template for Header view model:
<%: Html.EditorFor(x => x.Header) %>
The editor template (EditorTemplates/HeaderViewModel.ascx)
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<HeaderViewModel>" %>
<% ViewData.TemplateInfo.HtmlFieldPrefix = ""; %>
<%: Html.EditorFor(x => x.Search) %>
The result:
<input type="text" value="" name="Search" id="Search" />
If I remove the line
<% ViewData.TemplateInfo.HtmlFieldPrefix = ""; %>
the result is:
<input type="text" value="" name="Header.Search" id="Header_Search" />
Is there another way to achieve the same - render the names of the control without prefix?
I was thinking about a helper:
public static MvcHtmlString EditorWithoutPrefix<TModel, TValue>(
this HtmlHelper<TModel> html, TValue value)
{
var htmlHelper =... // create new HtmlHelper<TValue> and set it's model to be 'value' argument
return htmlHelper.EditorForModel();
}
and use it:
<%: Html.EditorWithoutPrefix(Model.Header) %>
but it is throwing exceptions.
Or maybe you know another elegant way to render names without prefix?
You could use the proper overload:
<%: Html.EditorFor(x => x.Search, "SearchViewModel", "") %>

Problems Passing ViewModel with data from View to Controller

Ok, here is my problem. I am trying to pass a view model of mine, which has multiple list in it, to my view. Then in my view i need to edit the different list. Then on my post i need to save the edits. Although, when i pass my viewmodel back to my post, it is empty! Can somebody explain what i am doing wrong? i am not worried about saving the information right now, i am just worried about getting the data back to the controller. The ViewModel is null when i hit that portion.
Controller
public ActionResult ManageNewsArticles()
{
NewsViewModel newsViewModel = new NewsViewModel();
newsViewModel.ListBreakingNews = db.NewsArticles.Where(n => n.PageSetupID == 1).ToList<NewsArticle>();
newsViewModel.ListMainArticle = db.NewsArticles.Where(n => n.PageSetupID == 2).ToList<NewsArticle>();
newsViewModel.ListSubNews1 = db.NewsArticles.Where(n => n.PageSetupID == 3).ToList<NewsArticle>();
newsViewModel.ListSubNews2 = db.NewsArticles.Where(n => n.PageSetupID == 4).ToList<NewsArticle>();
newsViewModel.ListSubNews3 = db.NewsArticles.Where(n => n.PageSetupID == 5).ToList<NewsArticle>();
return View(newsViewModel);
}
[HttpPost]
public ActionResult ManageNewsArticles(NewsViewModel newsViewModel)
{
if (ModelState.IsValid)
{
db.SaveChanges();
return RedirectToAction("Admin");
}
return View(newsViewModel);
}
here is my View
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TrueNews.ViewModels.NewsViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Manage News Articles
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Manage News Articles</h2>
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm(Model)) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<%: Html.EditorForModel(Model) %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to Admin Controls", "Admin") %>
</div>
</asp:Content>
NewsViewModel
public class NewsViewModel
{
public List<NewsArticle> ListBreakingNews { get; set; }
public List<NewsArticle> ListMainArticle { get; set; }
public List<NewsArticle> ListSubNews1 { get; set; }
public List<NewsArticle> ListSubNews2 { get; set; }
public List<NewsArticle> ListSubNews3 { get; set; }
} // End of Class
I couldn't get your view to display correctly using the EditorForModel syntax, however I did replicate your problem and found this article which provides one way of solving it:
http://weblogs.asp.net/nmarun/archive/2010/03/13/asp-net-mvc-2-model-binding-for-a-collection.aspx
I created a quick table NewsArticle containing Id and Stuff columns and then had the following form in the view:
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<% for (int i = 0; i < Model.ListBreakingNews.Count; i++)
{ %>
<div>
Id</div>
<div>
<%= Html.TextBox(string.Format("ListBreakingNews[{0}].Id", i), Model.ListBreakingNews[i].Id) %>
</div>
<div>
Name</div>
<div>
<%= Html.TextBox(string.Format("ListBreakingNews[{0}].Stuff", i), Model.ListBreakingNews[i].Stuff) %>
</div>
<% } %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
and the items in the list were passed when posted. I also found that previously you had Html.BeginForm(Model) which when I added it caused nothing to be passed through in the view model - I am only a newbie to MVC so I don't know why this happens but my code below worked only with Html.BeginForm() and not Html.BeginForm(Model).
I hope this helps.

How to bind list to a controller

How to bind a list to a controller?
Code in my controller, HTTPGET and HTTPPOST :
public ActionResult Client(string id)
{
List<Customer> Contacts = WebService.GetContactsOfClient(id);
return View(Contacts);
}
[HttpPost]
public ActionResult Client(List<Customer> custs)
{
// custs = null!!! Why??
return View();
}
Code in my view :
<% using (Html.BeginForm("Client", "Formulaire", FormMethod.Post, new { id = "form" })) { %>
<% foreach (var item in Model) { %>
<%:item.MiseAjour %><br />
<% } %>
<input type="submit" />
<% } %>
Thank you all...
If you want to get some values back in your POST action you need to POST them which is achieved using input fields:
<% using (Html.BeginForm() { %>
<%= Html.EditorForModel() %>
<input type="submit" value="OK" />
<% } %>
This outlines what you're trying to achieve.
Model Bind to a List<> when Posting JavaScript Data Object
If you have specific questions, post them
Your view is not rendering any html input elements which are required if you want the model binder to put your model back together on the post. See this answer here which shows binding to a collection and posting to back. Also check out this post for binding to a dynamic list.
Solution for me :
<legend>Contacts</legend>
<% for (int i = 0; i < Model.Contacts.Count; i++) { %>
<%: Html.EditorFor(model => model.Contacts[i],"Contact") %>
<% } %>
With this, I bind my list to controller!
Thank you all!!

ASP.NET MVC newbie: getting/passing data in dropdown form for multiple objects in an edit View

Using the Authors/Books catalog example, let's say I want to edit the info for the books of a specific author.
When someone navigates to domain.com/Books/Edit/2, I want to display an edit view for all the books where Author_ID = 2. Among the various book info is the book category (fiction, non-fiction, textbook, whatever) These categories are in their own table and are referenced by a Category_ID.
What's the best way to set up the edit form?
Currently in my controller I have something like this:
public ActionResult Edit(int id)
{
IQueryable<Book> books = bookRepository.FindBooksForAuthor(id);
return View(books);
}
And in my partial view:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IQueryable<Authors.Models.Book>>" %>
<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<%var i = 0;
foreach (var book in Model)
{%>
<p>
<label for="Category_ID">Category_ID:</label>
<%= Html.TextBox("Category_ID", book.Category_ID)%>
<%= Html.ValidationMessage("Category_ID", "*")%>
</p>
<p>
<label for="Description">Description:</label>
<%= Html.TextBox("Description", book.Description)%>
<%= Html.ValidationMessage("Description", "*")%>
</p>
<%i++;
} %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
Is my Inherits set properly at the top of the view since I'm passing an IQueryable object?
More importantly, how do I get the Category_ID field to be a DropDown with the correct category selected?
Can I just send the data for the dropdown to the view and figure out the selected item at the view level?
ViewData["categories"] = new SelectList(_db.BookCategories.ToList().OrderBy(b => b.Category_Title), "Category_ID", "Category_Title");
You could create view model class containing list of books and select list of categories:
public class BooksEditViewModel
{
public IQueryable<Authors.Models.Book> Books { get; set; }
public IQueryable<BookCategory> BookCategories { get; set; }
}
Then use BooksEditViewModel as view model
System.Web.Mvc.ViewUserControl<BooksEditViewModel>
and code dropdown with
Html.DropDownList("Category_ID", new SelectList(Model.BookCategories,"Category_ID", "Category_Title",book.Category_ID);
You should also read about list binding:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Resources