Cannot get Editor Templates working in MVC3 - asp.net-mvc

I am converting a MVC2 app to MVC3.
I cannot seem to join up a View with an Editor Template.
So in my View the code is;
#model IEnumerable<ITOF.Models.LatestContractNumber>
#{
ViewBag.Title = "EditContractNumbers";
Layout = "~/Views/Shared/_Layout.cshtml";
}
using (Html.BeginForm())
{
<fieldset>
<legend>Edit Latest Contract Numbers</legend>
<div style="width:90%">
<div style="width:45%; float:left; vertical-align:top;">
<p>A contract number is in a format: AA/B999</p>
<p>Where AA is a 2 character code for the division, for example CD.</p>
<p>Where B is a letter for the contract, usually the first letter of the contract title.</p>
<p>999 is a 3 digit number.</p>
<p>The 999 number depends on the B letter. </p>
<p>Initially all the numbers start as 1 and are incremented each time the B letter is used.</p>
<p>This pages sets the initial 999 number for each B letter.</p>
<p>Once the application is up and running, this page should be used initially and then no more.</p>
#if (TempData["Message"] != null && TempData["Message"].ToString().Trim().Length > 0)
{
<p class="success">#TempData["Message"].ToString()</p>
}
</div>
<div style="width:45%; float:right; vertical-align:top;">
<div>
<table>
<tr>
<td>Letter</td>
<td>Number</td>
</tr>
#Html.EditorForModel(Model)
</table>
</div>
<div style="padding-top:20px">
<input type="submit" value="Submit Changes" />
</div>
</div>
</div>
</fieldset>
}
And my Editor Template is in the correct folder and now looks like;
#model ITOF.Models.LatestContractNumber
<tr>
<td>
#Html.HiddenFor(model => model.LatestContractNumberId)
#Html.DisplayFor(model => model.Letter)
</td>
<td>
#Html.TextBoxFor(model => model.Number, new { style = "width:30px" })
</td>
</tr>
The Model in #Html.EditorForModel(Model) relates to #model IEnumerable at the top of the page.
In the Editor Template, this should translate to a loop of ITOF.Models.LatestContractNumber, hence at the top of the template I have put #model ITOF.Models.LatestContractNumber

First, you are calling EditorForModel with a model of type IEnumerable<ITOF.Models.LatestContractNumber> (which is the Model for your View) but you should be calling it with ITOF.Models.LatestContractNumber (which is the Model your template expects to see/get). You need a foreach somewhere in there
foreach(ITOF.Models.LatestContractNumber number in Model) {
#Html.EditorForModel(number)
}
Second, not 100% sure, but EditorTemplates should be in Shared folder under the Views folder. So ~/Views/Shared/EditorTemplates/ and should be named after the class name so LatestContractNumber.cshtml.
HTH

The question turned out to be a red herring. The real problem was that having migrated to MVC and started prefixing partial views with _, the name of the Editor Template was invalid.
Thanks to Russ Cam for asking the right questions that got me the answer.

Related

Pass one of many objects from view to controller

In an ASP.Net Core web app, I'm passing a List<Parameter> to a view via ViewBag.Parameters. The view also has a SelectParameterViewModel that consists of one property only, an int ParameterId. The objects are then displayed in a table like so (I skipped straight to tbody as the view works fine from a cosmetic perspective):
#model WebApp.Models.ReportViewModels.SelectParameterViewModel
[...]
<tbody>
#foreach (var item in ViewBag.Parameters)
{
<tr>
<td class="col-xs-1">
<form asp-controller="Report" asp-action="Launch">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input asp-for="#Model.ParameterId" type="hidden" value="#item.Id" />
<input type="submit" class="btn btn-primary btn-sm" value="Select" />
</form>
</td>
<td class="col-xs-8">#item.Description</td>
<td class="col-xs-3">
<a asp-action="Edit" asp-route-id="#item.Id">Edit</a> |
<a asp-action="Details" asp-route-id="#item.Id">Details</a> |
<a asp-action="Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
</tbody>
I would like the user to click on the button of the chosen table-row and submit either that row's Parameter (preferred) or the Parameter's Id, like I tried to do in this case. I would then do the same with the <a> tags on the third column. The point is that I don't want the user to simply type /Report/Launch/id and run the report, as each user would have its own sets of parameters and should not be allowed to use any of the others', and the ViewBag only contains the list of its Parameter. I can change the view model and/or the receiving controller's argument depending on the solution, both would work. I tried a few versions of the above html, but none would work, while the above returns ParameterId with value 0.
I couldn't find a similar scenario in other SO questions. There were some solutions that involved JavaScript and, correct me if I'm wrong, but that would still show the ParameterId value in the webpage source. While that would be sort-of-ok in this specific case, I have other cases where I definitely would not want that to happen.
What would be an elegant solution to achieve this? Thank you!

Counter in EditorTemplates ASP.NET MVC

I would like to display an incremental value in editor template.
My Razor View-
#model List<Target>
<form>
#Html.EditorForModel()
<input type="submit">
</form>
My Editor Template -
#model Target
<div>
<p>SL No.: ?? </p>
#Html.TextBoxFor(m => m.Quantity)
</div>
My question is how can I increment a variable i.e. counter here?

how to create an editable partial view in ASP.NET MVC

I'm currently trying to create an edit view that consists of some user profile details and a list of roles that the user is assigned to. I thought that I could create an editable parent view that would contain all of the user profile textboxes and insert a partial view that would hold checkboxes for all of the roles.
However, I have found that when I try to update the textboxes in the partial view, those changes are not reflected in the parent model. Apparently, the partial view gets its own ViewData dictionary that is isolated from the parent's.
I was wondering if anyone had a solution that would allow me to pass changes made to a checkbox in a partial view back to the model of the parent. I have included some sample code below. Here is the parent view:
#model MvcWebsite.ViewModels.UserProfileEditViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>UserProfile</legend>
#Html.HiddenFor(model => model.UserId)
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
... other properties of the user profile omitted for brevity
<div class="editor-field">
#Html.Partial("RoleCheckBoxEditTable", Model.Roles)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
We then have the child partial view:
#model List<MvcWebsite.ViewModels.RoleViewModel>
<table>
<tr>
#{
int count = 0;
foreach (var roleViewModel in Model)
{
if(count++ % 3 == 0)
{
#: </tr> <tr>
}
<td>
#Html.HiddenFor(r => roleViewModel.RoleName)
#Html.HiddenFor(r => roleViewModel.RoleId)
#Html.CheckBoxFor(r => roleViewModel.Assigned)
</td>
<td>
#Html.LabelFor(r => roleViewModel.Assigned, roleViewModel.RoleName)
</td>
}
}
</tr>
</table>
What I would like is to find a way to make both of these views update the same model, which could then be sent to the controller. Any help would be greatly appreciated.
I got around this problem by eliminating Partial Views entirely and replacing them with Display and Editor Templates.
Take the stuff from your View for that list of RoleViewModel, and create a new View under Views/Shared/EditorTemplates with the name RoleViewModel.cshtml (you might have to create the EditorTemplates directory first if you've never used it before).
On your top-level view, change Partial to EditorFor like this: #Html.EditorFor(model => model.Roles)
This approach works for me, and it preserves the baked in validation that I get by using the IValidatable interface on my Models.

Incorrect Razor syntax

I'm having some trouble understanding how #Razor works in a view. The code below is my view where a user can create a new post (I'm creating a forum)
What I want to do is remove the <Fieldset>
My problem is that I can't change the code I've marked.
#model Forum3.Models.Posts
<h2>CreatePost</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
//--- CAN'T EDITED ----
<fieldset>
<legend>Post</legend>
#*SET TopicID*#
#{
Html.HiddenFor(model => model.TopicId);
#Html.Hidden("TopicId",ViewData["currentTopicId"]);
}
//----END----
<div class="editor-label">
#Html.LabelFor(model => model.Text)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Text)
#Html.ValidationMessageFor(model => model.Text)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
If I remove the <Fieldset> and <Legend> I get this error on my HiddenFor code:
Parser Error Message: Unexpected "{" after "#" character. Once inside the body of a code block (#if {}, #{}, etc.) you do not need to use "#{" to switch to code.
If I then remove the #{...} to look like this:
#Html.HiddenFor(model => model.TopicId);
#Html.Hidden("TopicId",ViewData["currentTopicId"]);
I'll get an error when I click Create because the TopicId is not being set to my currentTopicId (This also happens if I leave the <fieldset> in)
I have no idea what going on here. Any ideas?
I'm not having any errors with that once you remove the surrounding block and use the # on both fields. However, one thing you do have that's probably messing up your post - you have both a Hidden and a HiddenFor for the same property. So if you look at the rendered markup, you'll see it there twice, so it gets posted twice (I'm not sure which one it assigns to the posted model).
The HiddenFor is all you need - just make sure your model contains the TopicId value, and you don't need it in ViewData at all, so you can get rid of that second one.

Make a ASP.Net MVC Site, filtering the content

I recently started an MVC project to query and to report the company's users, everything seemed to be fine, except when I made a finduser form, I got all stuck!
you see I want the operator to be able to find the appropriate user by entering either hos pin,serial or calling number but the action links I make for the search operation all fail because they are made at the form_load time and so the empty string of text boxes get injected to them.
So my requests are:
How to make this filters work.
Now a little Ajax textbox suggester on user pin or serial would be great, please gimme a hint or two.
<table>
<tr>
<td>
PIN:</td>
<td>
<asp:TextBox ID="txt_pin" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
Serial Number:</td>
<td>
<asp:TextBox ID="txt_sn" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
CallingNumber:</td>
<td>
<asp:TextBox ID="txt_callingNo" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
CalledThisNumberToday:</td>
<td>
<asp:TextBox ID="txt_calledNo" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
</td>
</tr>
</table>
<%:Html.ActionLink("Search for user", "Details", new { pin = txt_pin.Text })%>
<asp:TextBox>? In an ASP.NET MVC application? I am afraid you got it all wrong.
I would recommend you going through the getting started tutorials here in order to learn the basic concepts of ASP.NET MVC: http://asp.net/mvc
In ASP.NET MVC you use models and in the views you use helpers in order to generate input fields.
So in your case you could design a view model:
public class SearchViewModel
{
public string Pin { get; set; }
public string SerialNumber { get; set; }
public string CallingNumber { get; set; }
public string CalledThisNumberToday { get; set; }
}
then you could design a controller action which will pass this view model to the view for rendering the search form:
public ActionResult Index()
{
var model = new SearchViewModel();
return View(model);
}
and finally in your strongly typed view you would use HTML helpers:
<%# Page
Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<AppName.Models.SearchViewModel>" %>
<asp:Content ID="MainContent" ContentPlaceHolderID="MainContent" runat="server">
<%= Html.ValidationSummary() %>
<% using (Html.BeginForm()) { %>
<div>
<%= Html.LabelFor(x => x.Pin) %>
<%= Html.EditorFor(x => x.Pin) %>
</div>
<div>
<%= Html.LabelFor(x => x.SerialNumber) %>
<%= Html.EditorFor(x => x.SerialNumber) %>
</div>
<div>
<%= Html.LabelFor(x => x.CallingNumber) %>
<%= Html.EditorFor(x => x.CallingNumber) %>
</div>
<div>
<%= Html.LabelFor(x => x.CalledThisNumberToday) %>
<%= Html.EditorFor(x => x.CalledThisNumberToday) %>
</div>
<p><input type="submit" value="Search for user" /></p>
<% } %>
</asp:Content>
and the final step would be to implement the controller action that will perform the search and to which this form will be subimtted:
[HttpPost]
public ActionResult Index(SearchViewModel model)
{
if (!ModelState.IsValid)
{
// the model was not valid => redisplay the form
// so that the user can fix his errors
return View(model);
}
// TODO: perform the search
...
}
Along with Darin's excellent answer (which you really need to follow in order to do things the "MVC" way...), you can also do AJAX filtering on a table using jQuery. There's even a plugin for jQuery that makes live table filtering as simple as adding a script reference and a textbox, then a couple lines of Javascript code to wire up the search. This will end up looking something like this:
<script language="javascript" type="text/javascript" src="/Scripts/jquery.js"></script>
<script language="javascript" type="text/javascript" src="/Scripts/jquery.uitablefilter.js"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function () {
$("#myFilter").keyup(function () {
$.uiTableFilter($("#myTable"), $(this).val());
});
});
</script>
<input type="text" id="myFilter" />
<table id="myTable">
...
</table>
As you start typing in the filter box, the table will be automatically filtered to only show rows that contain the specified value.
As for auto-complete suggestions, you might want to look into jQUery UI - they've got
support for auto-complete using several different mechanisms to provide the hint values.

Resources