MVC 2 Templates - Is it possible to redirect the current editor template to its display template based on metadata? - asp.net-mvc-2

For a project I'm working I've been asked to redirect an editor template to its display template based on metadata that is provided with the model.
Now I was looking at a way to do it before it hits the editor template but that seems to cause more issues than it is worth, at least with how the system has been architected.
The simplest example is the string editor, its a simple textbox but if IsReadOnly is set we want it to only show up as text, not a disabled textbox.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
if (this.ViewData.ModelMetadata.IsReadOnly)
{
Response.Write(Html.DisplayForModel());
}
else if (this.ViewData.ModelMetadata.ShowForEdit)
{
<%= Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line" }) %>
<% } %>
So far the only real solution I can find is to copy the display template into the editor template. Does anyone have any ideas how I can do something that will work without replicating more code?

Why not do this outside of the editor template itself? Define an extension method that checks if the property is read only then either shows the edit or display template. You'll need to copy the PropertyHelper class from this answer.
public MvcHtmlString DisplayOrEditFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> selector)
{
var property = PropertyHelper<TModel>.GetProperty(selector);
if(property.CanWrite)
{
return helper.EditorFor(selector);
}
return helper.DisplayFor(selector);
}
Then in your view just do
<%: Html.DisplayOrEditFor(x => x.Name) %>
Only drawback is this won't work with Html.EditorForModel().

Related

ASP.Net MVC 2 - reusing Controller/View by indicating return Action and Controller in params

Problem:
In my application I have provide the user a selection screen for something in several places (that is, the same selection screen has to be used in several actions).
Solution:
I've come up with the following solution: pass the return action and controller to action which handles the entity selection.
Example:
Suppose that there are several places in the application where the user has to select an instance of SomeEntity, so I added the following action to that entity controller:
public class SomeEntityController : Controller
{
/* ... */
public ViewResult Select(string returnAction, string returnController)
{
var selectableEntities = ...;
return View(
new SelectionViewModel<SomeEntity>
{
Entities = selectableEntities,
ReturnAction = returnAction,
ReturnController = returnController,
});
}
}
In the view for that action (Views/SomeEntity/Select.aspx) I put something like this:
<table>
<tr>
<th>Select</th>
<th>SomeProperty<th>
</tr>
<% foreach (var entity in Model.Entities) { %>
<tr>
<td>
<%: Html.ActionLink("Select", Model.returnAction, Model.returnController, new { entityId = entity.id }) %>
</td>
<td><%: entity.SomeProperty %></td>
</tr>
<% } %>
</table>
Then, if I need the user to select a SomeEntity in other controller I can do this:
public class OtherController : Controller
{
public ActionResult SelectSomeEntity()
{
return RedirectoToAction("Select", "SomeEntity", new { returnAction = "ActionThatNeedsAnEntity", returnController = "Other" });
}
public ActionResult ActionThatNeedsAnEntity(int entityId)
{
// here I can use the selected entity
}
}
The last piece of code is just an example of how to use the SomeEntity selection action. Instead of the SelectSomeEntity action, there could be a more complex action which performs some checks to see if an entityId is already selected (e.g. stored in the session) and then decide whether to call SomeEntity/Select or not
Question:
The above works fine, but I’m new to ASP.Net MVC 2 so I don’t know if there is other (standard) solution for this.
Is this approach correct/neat? Have you solve this same situation differently?
I could be misunderstanding your problem, but I think that Partial Views would be the "standard" solution you're looking for.
Partial Views are just that, small views that can be inserted into other views. Things like entry forms or displays of data can be put into a partial view and added to the regular view. They greatly simplify code.
They're simple to make. When you go to make a regular view, just check the "partial view" box in the window (i. e. after right-clicking in the solution explorer and selecting the "add view" option). You can then throw this into any full view by using <%: Html.Partial("myPartialView") %>. You can easily pass a Model to the partial view as well by doing <%: Html.Partial("myPartialView", Model) %>

Post with MVC 2, including values outside of form

I'm pretty new to MVC 2 and I'm having problems figuring out how to post values that lies outside the form.
This is my code very simplified:
<input type="text" id="TextboxOutsideForm"/>
<% using (Html.BeginForm("Edit", "Home", FormMethod.Post)) {%>
<%: Html.ValidationSummary(true) %>
<%: Html.TextBoxFor(model => model.Stuff) %>
<input type="submit" value="Save" />
<% } %>
TextboxOutsideForm can be changed by the user and when pushing save I want to include the value from this control to the action in the controller. It would also be great if i could use model binding at the same time.
This would be great:
[HttpPost]
public ActionResult Edit(int id, Model model, string valueFromTextbox)
Or just a FormCollection and a value..
I know that in this simplified example i could just put the textbox inside of the form, but in real life the control is 3rd party and is creating loads of of other stuff, so i just need to grab the value with jquery maybe and post it..
Is this possible?
Thanks
you can have a look at this question that explains how to dynamically create the form and submit it. you can hijeck the submit event and add value into form dynamically
$(function() {
$('form').submit(function() {
$(this).append($("#ValueOutsideForm"));
return true;
});
});
this way u don't have to rely on ajax and u can post ur form synchronously.
If you use a normal form post only inputs inside the form will be sent to the server. To achieve what you are looking for you will need to submit the form using AJAX. Example:
$(function() {
$('form').submit(function() {
$.ajax{
url: this.action,
type: this.method,
data: $(this).clone().append($('#TextboxOutsideForm').clone()).serialize(),
success: function(result) {
alert('form successfully posted');
}
});
return false;
});
});
Also don't forget to give a name to the input field which is outside the form.

ASP.NET MVC - How can I set the Default Value in a Strongly Typed TextArea?

I'm trying to make a TextArea have a default value.
<%: Html.TextAreaFor(Function(model) model.Description, 5, 10, New With {.Value = "Description: "})%>
This works properly in a TextBoxFor but doesn't work in a TextAreaFor
Am I missing something very obvious?
You can specify the value for Description property in the controller action when creating the model, and pass that model to the View:
public ViewResult Create()
{
var model = new MyPageModel()
{
Description = "Description: ";
}
return View(model);
}
In the view:
<%: Html.TextAreaFor(model.Description) %>
Setting the value won't work as the contents of a TextArea are specified between the opening and closing tags, not as an attribute.

Submit PartialView

I have a masterpage that render's the following PartialView:
<% Html.RenderPartial("AddPage); %>
AddPage Controller looks like this:
public class PagController : Controller
{
[HttpGet]
public PartialViewResult AddPage()
{
return PartialView();
}
[HttpPost]
public PartialViewResult AddPage(FormCollection forms)
{
//some logic here
// some view data here
return PartialView();
}
}
And the view looks like this:
<% using (Html.BeginForm("AddPage", "Page", FormMethod.Post, new { ID = "PageManager" })){%>
<%= Html.TextBox("txtAddPage") %>
<input type="submit" value="AddPage" />
<%} %>
My issue is, that when i hit submit i get redirect to : http://localhost:1234/Page/AddPage, when instead i just want the partialview to be submitted, and return some basic view data if needed, as well as stay on the same page.
Am i having a blonde moment here? cause i know i have done this before
EDIT - This partial view is rendered in multiple pages, not just one.
Fullpage postback solution
This is a bit tricky since you have to know where to go back. I suggest you change your view and add two additional hidden fields (or one and parse its value - as you wish) and store current controller/action values in it.
You can then use this data in POST action to return a RedirectResult like:
return RedirectToAction("action_from_field", "controller_from_field");
Ajax postback solution
You can always submit your data using Ajax in which case your postback URL can be anything you want. In your case it should be to the current page URL. Edit: And Ajax would be the preferred solution in your particular case.
If you want to submit and only reload part of your page, you'll need to use AJAX. The most basic way to do this would be to render the partial view in an UpdatePanel.

Is there a good way to create render a blank slate for a page in ASP.Net MVC?

I have an index page which shows a paged list of data from a database. When that list is empty, I want to show a "blank slate" view that clearly indicates to the user where they are and what they can do there: "You can add a new item by clicking here" type of thing.
Is there a better/cleaner way to do this than just having a big if statement around the entire page?
<% if (Model.Items.Count > 0) { %>
normal view
<% }
else { %>
blank slate view
<% } %>
Good suggestions from Benjamin Anderson. In addition, you many want to look into the MVCContrib Grid (see the .Empty method)
http://www.jeremyskinner.co.uk/2009/02/22/rewriting-the-mvccontrib-grid-part-2-new-syntax/
<%= Html.Grid(Model.People).Columns(column => {
column.For(x => x.Id).Named("Person ID");
column.For(x => x.Name);
column.For(x => x.DateOfBirth).Format("{0:d}");
})
.Attributes(style => "width:100%")
.Empty("There are no people.")
.RowStart(row => "<tr foo='bar'>") %>
Other than redirecting to a different View in the controller, or using a different view engine, that is the best way.
A slightly cleaner alternative would be to use a Partial View for the grid and paging, but you'd still have an If clause in the middle of the view.
How about the following HtmlHelper where viewName is the name of a partial view. It is not exactly what you are after but may provide a start. What I would consider is that assuming you have a set list of actions that can be performed in the event of an empty list, you could create partial views that reflect that. Maybe one for each controller but named the same and can replace the "NoResultsView" argument.
Depending if you are using ViewModels in your project - you can create a naming convention to for result type views and this could further eliminate the need for the viewName argument
public static MvcHtmlString ResultsView<TModel> (this HtmlHelper helper, IList<TModel> items, string viewName) where TMdodel: class
{
if (items.Count() != 0)
{
return System.Web.Mvc.PartialExtensions.Partial(helper, viewName, items);
}
}
return return System.Web.Mvc.PartialExtensions.Partial(helper, "NoResultsView", items); // View is Shared
}