I wanted to remove repeated code from my 'edit' view forms by writing a method to generate the HTML for the field name, input box, and any validation messages. Here is an example of the default view code generated by the system:
<div class="editor-label">
<%: Html.LabelFor(model => model.dateAdded) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.dateAdded, String.Format("{0:g}", Model.dateAdded)) %>
<%: Html.ValidationMessageFor(model => model.dateAdded) %>
</div>
And here is what I started to write:
MvcHtmlString DataField(HtmlHelper h, Object m)
{
string s=h.TextBoxFor(m => m.dateAdded);
}
Now I know that won't work properly, it's just a start, but I get the error "'System.Web.Mvc.HtmlHelper' does not contain a definition for 'TextBoxFor' and no extension method 'TextBoxFor' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found".
Are you trying to write a custom HTML helper that would generate this HTML? I would recommend you using a custom editor template because what you have is primary markup. So you could have the following partial (~/Views/Shared/EditorTemplates/SomeViewModel.ascx):
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.SomeViewModel>" %>
<div class="editor-label">
<%: Html.LabelFor(model => model.dateAdded) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.dateAdded, String.Format("{0:g}", Model.dateAdded)) %>
<%: Html.ValidationMessageFor(model => model.dateAdded) %>
</div>
and then whenever you have a strongly typed view to SomeViewModel simply:
<%= Html.EditorForModel() %>
or if you have a property of type SomeViewModel:
<%= Html.EditorFor(x => x.SomePropertyOfTypeSomeViewModel) %>
which would render the custom editor template.
As far as the helper is concerned the proper signature would be:
using System.Web.Mvc;
using System.Web.Mvc.Html;
public static class HtmlExtensions
{
public static MvcHtmlString DataField(this HtmlHelper<SomeViewModel> htmlHelper)
{
return htmlHelper.TextBoxFor(x => x.dateAdded);
}
}
Related
The command
mix phx.gen.html Blog Post posts title body:text
generates among other files the template form.html.eex for the add/edit posts form, which looks like this:
<%= form_for #changeset, #action, fn f -> %>
<%= if #changeset.action do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below.</p>
</div>
<% end %>
<div class="form-group">
<%= label f, :title, class: "control-label" %>
<%= text_input f, :title, class: "form-control" %>
<%= error_tag f, :title %>
</div>
<div class="form-group">
<%= label f, :body, class: "control-label" %>
<%= textarea f, :body, class: "form-control" %>
<%= error_tag f, :body %>
</div>
<div class="form-group">
<%= submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
The form_for function gets #action argument which is not present in the PostController, so where does it come from? I couldn't find.
In my installation I have a plug MyProject.Locale which redirects all unlocalized requests to localized ones like:
/posts --> /de/posts
And I have the :locale scope in my router
scope "/:locale", MyProject.Web, as: :locale do
pipe_through :browser
get "/", PageController, :index
resources "/posts", PostController
end
The default #action doesn't take the scope's locale chunk into account and sends the POST request to the /posts url, which is redirected by my MyProject.Locale plug to /de/posts as GET request (it uses the function Phoenix.Controller.redirect(to:...)). I want the form to send the POST request to the localized path.
So, can we override this #action argument for all resources somewhere in one place or do we have to provide it in each controller in render function call
render(conn, "new.html", changeset: changeset, action: action)
or the only option is to change the form templates for all resources?
I have this form:
<% #page_title = "Delete Technician: #{#technician.name}" %>
<%= link_to("<< Back to List", {:action => 'list', :id => #technician.id}, :class => 'back-link') %>
<div class="technician delete">
<h2>Delete Technician</h2>
<%= form_for(:technician, :url => {:action => 'destroy', :id => #technician.id}) do |f| %>
<p>Are you sure you want to permanently delete this technician?</p>
<p class="reference-name"><%= #technician.name %></p>
<div class="form-buttons">
<%= submit_tag("Delete Technician") %>
</div>
<% end %>
</div>
when I click on the submit button this is the url that I get:
www.site.com/technicians/1
instead of
www.site.com/technicians/destroy/1
am I not using the form_for helper correctly or is it a configuration somewhere?
You're making this more complicated than it needs to be. There is no reason for a form when a link or a button would do. Why not just this
<p>Are you sure you want to permanently delete this technician</p>
<div> <%= link_to "Delete", technician_path(#technician), :method => :delete %> </div>
That's it.
I Have 2 views. ProductForm.aspx and Category.ascx.
CategoryForm is a Partial View. I Call the Category.ascx from the ProductForm with EditorFor(model => model.Category) . In this partial view, there is a DropdownlistFor with all the category. The problem is the Selected Value for a specific Product Category. The selected value dosen't work.
Why ?
Here is what I have in my ProductForm
<div class="editor">
<div class="editor-label">
<%: Html.LabelFor(model => model.ProductInfo.ProductName) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ProductInfo.ProductName)%>
<%: Html.ValidationMessageFor(model => model.ProductInfo.ProductName)%>
</div>
</div>
<%: Html.EditorFor(model => model.ProductInfo.Category, new { CategoryList = Model.CategoryList })%>
In Category.ascx
<div class="editor-field">
<%:Html.DropDownListFor(model => model.CategoryID, (IEnumerable<SelectListItem>)ViewData["CategoryList"])%>
</div>
You can assign the name attribute of your DDL to whatever your CategoryID/foreign key is called in your Products table. Then your DDL will automatically select that category, due to how default binding works.
One example:
<%: Html.DropDownList("Book.GenreID" , Model.GenresSelectList )%>
and the resulting html:
<select id="Book_GenreID" name="Book.GenreID">
<option value="2">Horror</option>
<option selected="selected" value="3">Literature</option>
<option value="1">Science Fiction</option>
</select>
or:
<%: Html.DropDownListFor(model => model.Book.GenreID, Model.GenresSelectList )%>
I have two tables: Area and Boss.
An Area has a Boss, it has a foreign key relationship of Area.IDBoss and that is Boss.ID. I hope I'm explaining my question properly. :P
As of now I can manually type in a number in the textbox and it saves correctly, I can also display the name correctly because I'm using Entity Framework, something like "item.Boss.ID" in the View and it works fine.
I really need to display a DropDownList though.
I'm guessing I have to return a collection of available "Boss" rows from my database using the ViewData[] dictionary, but I'm honestly stuck. :D
Any suggestions? This is a very simple use case, so hopefully I don't spend too much time on it. If you guys need any code from my part just say, but I doubt it would help for such a simple question.
As always this site is fantastic, thanks for the help.
Edit:
Maybe posting some code would help. Right now I'm receiving this error:
Edit 2:
Edited with more recent code that is still not working.
There is no ViewData item of type
'IEnumerable' that has
the key 'Jefes'.
And here's the code:
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.IDJefe) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Jefes", (SelectList)ViewData["Jefes"]) %>
<%--<%: Html.TextBoxFor(model => model.IDJefe) %>--%>
<%: Html.ValidationMessageFor(model => model.IDJefe) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Nombre) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Nombre) %>
<%: Html.ValidationMessageFor(model => model.Nombre) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
JefeRepository jefeRepo = new JefeRepository();
var jefes = jefeRepo.FindAll().OrderBy(x => x.Nombre);
var jefeList = new List<SelectListItem>();
foreach (var jefe in jefes)
{
jefeList.Add(new SelectListItem()
{
Text = jefe.Nombre,
Value = jefe.ID.ToString()
});
}
ViewData["Jefes"] = jefeList.AsEnumerable();
By the way I translated some variable names from Spanish to English so they would make more sense, sorry for the confusion.
Edit 3:
If you guys need any more information please let me know. I've looked over every line of code but it just doesn't work.
You can put an IEnumerable<SelectListItem> in your ViewData[].
var bossDropDown = from boss Area.Boss
select new SelectListItem
{
Value = boss.ID,
Text = boss.Name
};
ViewData["BossDropDown"] = bossDropDown;
and in your View you can call directly your DropDown like this
<%=Html.DropDownList("BossDropDown", (SelectList)ViewData["BossDropDown"]) %>
EDIT:
With your code, try to change this line
ViewData["jefes"] = jefeList;
to this
ViewData["jefes"] = jefeList.AsEnumerable();
In your view, Change this line
<%: Html.DropDownList("IDJefe", (SelectList)ViewData["jefes"]) %>
to this
<%: Html.DropDownList("jefes", (SelectList)ViewData["jefes"]) %>
since, the name you are calling in your dropdownlist should be the Key of your ViewData that has the IEnumerable<SelectListItem>
do this
JefeRepository jefeRepo = new JefeRepository();
var jefes = jefeRepo.FindAll().OrderBy(x => x.Nombre);
List<SelectListItem> jefeList = new List<SelectListItem>();
foreach (var jefe in jefes)
{
jefeList.Add(new SelectListItem()
{
Text = jefe.Nombre,
Value = jefe.ID.ToString()
});
}
ViewData["Jefes"] = jefeList;
then
<div class="editor-label">
<%: Html.LabelFor(model => model.IDJefe) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Jefes",ViewData["Jefes"] as List<SelectListItem>, "Select a boss ...") %>
<%: Html.ValidationMessageFor(model => model.IDJefe) %>
</div>
your mistake: you cant converst a List to a Selectlist, this doesnt work.
but honestly, i'd do it like this:
JefeRepository jefeRepo = new JefeRepository();
var jefes = jefeRepo.FindAll().OrderBy(x => x.Nombre);
ViewData["Jefes"] = new SelectList(jefes,"ID", "Nombre");
then
<div class="editor-label">
<%: Html.LabelFor(model => model.IDJefe) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Jefes",ViewData["Jefes"] as SelectList, "Select a boss ...") %>
<%: Html.ValidationMessageFor(model => model.IDJefe) %>
</div>
let me know which one works best for ya ;)
I am looking for best practices conforming to the MVC design pattern.
My Entities have the following relationship.
tblPortal PortalId PrortalName
tblPortalAlias AliasId PortalId HttpAlias
Each Portal can have many PortalAlias.
I want to Add a New Portal and then Add the associated PortalAlias.
I am confused on how I should structure the Views and how I should present the Views to the user. I am looking for some sample code on how to accomplish this.
My thoughts are first present the Portal View, let the user add the Portal. Then click the Edit link on the Portal List View and on the Portal Edit View let them Add the PortalAlias.
If so, what should the Edit View look like?
So far I have:
Edit View
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MyProject.Mvc.Models.PortalFormViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit</h2>
<% Html.RenderPartial("PortalForm", Model); %>
<div>
<%= Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
PortalForm
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyProject.Mvc.Models.PortalFormViewModel>" %>
<%= Html.ValidationSummary("Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<%= Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%= Html.LabelFor(model => model.Portal.PortalId) %>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.Portal.PortalId) %>
<%= Html.ValidationMessageFor(model => model.Portal.PortalId) %>
</div>
<div class="editor-label">
<%= Html.LabelFor(model => model.Portal.PortalName) %>
</div>
<div class="editor-field">
<%= Html.TextBoxFor(model => model.Portal.PortalName) %>
<%= Html.ValidationMessageFor(model => model.Portal.PortalName) %>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
Alias<br /><%-- This display is for debug --%>
<% foreach (var item in Model.PortalAlias) { %>
<%= item.HTTPAlias %><br />
<% } %>
PortalFormViewModel
public class PortalFormViewModel
{
public Portal Portal { get; private set; }
public IEnumerable<PortalAlias> PortalAlias { get; private set; }
public PortalFormViewModel()
{
Portal = new Portal();
}
public PortalFormViewModel(Portal portal)
{
Portal = portal;
PortalAlias = portal.PortalAlias;
}
}
Hopefully you've found an answer to this elsewhere, although based on how difficult it is to find information about this online, it's probably unlikely ...
An MSDN blog linked over to ASP.NET MVC, Entity Framework, Modifying One-to-Many and Many-to-Many Relationships (there's a link to the previous in the series in the first paragraph).
But Editing a variable length list, ASP.NET MVC 2-style seems a little better (and includes sample code).