How to render an HTML attribute from a Razor view - asp.net-mvc

I would like to use the same partial view for create, edit, and details views, to avoid duplicating the fieldset structure for an entity. Then, depending on which view renders the partial view, I would like to add a class of "read-only" to a div surrounding my fieldset and handle making the actual input fields read-only on the client, using css or jQuery, or whatever. How can I specify from my Razor view that I need this class added to the "item-details" div?
<div class="item-details">
<fieldset>
<legend>Product Details</legend>
#Html.HiddenFor(model => model.DetailItem.ProductId)
<div class="editor-label">
#Html.LabelFor(model => model.DetailItem.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DetailItem.Name)
#Html.ValidationMessageFor(model => model.DetailItem.Name)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</div>

I would generally have a variable on the model that would indicate if your data is editable, and use it like:
<div class="item-details #(Model.IsReadOnly ? "read-only" : "")">
If you don't have it on the model however, you can use the ViewBag. In your parent view:
#{
ViewBag.IsModelReadOnly = true;
}
In your partial view:
<div class="item-details #(ViewBag.IsModelReadOnly ? "read-only" : "")">
Your html output for a readonly view would be
<div class="item-details read-only">
You can then access the div via jQuery and do what you need: $(".read-only")...

Why not just use
#Html.DisplayFor(model => model.[whatEver Property you are trying to show])

Related

How do you create a MVC view for navigation properties (one to many, many to many)

I have two ASP.NET MVC EF entities sharing an association:
When I am editing or creating the User, I'd like to be able to attach one or more UserRoles. So, my User Create view looks like this:
#model MyModels.UserViewModel
#{ ViewBag.Title = "Create"; }
#using (Html.BeginForm())
{
<fieldset>
<legend>User</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Username)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Username)
#Html.ValidationMessageFor(model => model.Username)
</div>
<!-- how to create an editor to add/remove roles -->
<div class="editor-label">
#Html.LabelFor(model => model.UserRoles)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserRoles)
#Html.ValidationMessageFor(model => model.UserRoles)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Since Model.UserRoles is initially empty, I get nothing in this line #Html.EditorFor(model => model.UserRoles). I tried adding a drop-down where I can select existing roles using JavaScript (I guess retrieved as partial view?), but I don't know how to append a selected role to the fieldset so that it gets submitted.
How is this usually done in ASP.NET? I have been searching SO for a while but cannot find the appropriate solution.
To clarify, what I don't understand is how to add/append a new role to the client side, so that the submit button actually sends this inside the UserRoles list.
(edit) Ok, I've found this: How to add an item to a list in a ViewModel using Razor and .NET Core. It says that I should fetch a partial view which is written to look like the inputs which would be generated by #Html.EditorFor (with the next array index), and then append it using JavaScript.
That looks quite weird, is that really the correct approach?

How can I refer to a property of a model within a partial view?

In MVC4, with the Razor view engine, we have a group of items that we wish to group in a partial view, so that we can reuse the code. Currently our main views we have something along the lines of:
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Description, new { #id = "Common" })
<div class="FormValidationText">
#Html.ValidationMessageFor(model => model.Description)
</div>
</div>
We want to be able to use this logic but with a wide variety of models, not all of which will want to use the model.Description.
For example, we would like to use model Foo, and create the above for the Foo.Bar property, but we also want to be able to use the Hello model's Hello.World property. These are all of type string, as you would expect since we want to handle the text input from the textarea.
What edits do we need to make in the partial view to say "Use some specified property of any given model, and understand it to be a property of that model, to generate these items". How, subsequently, do we work with the #{Html.RenderPartial(...)} method to ensure that we have passed the models property?
Please forgive me if this seems a little confused, I am still learning MVC and Razor views.
These are all of type string, as you would expect since we want to
handle the text input from the textarea.
You could write an editor template: ~/Views/Shared/EditorTemplates/MyTemplate.cshtml:
#model string
<div class="editor-label">
#Html.LabelFor(model => model)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model, new { #id = "Common" })
<div class="FormValidationText">
#Html.ValidationMessageFor(model => model)
</div>
</div>
and then your views you could use like that:
#model Foo
...
#Html.EditorFor(x => x.Description, "MyTemplate")
or:
#model Hello
...
#Html.EditorFor(x => x.World, "MyTemplate")
and if you name your template ~/Views/Shared/EditorTemplates/string.cshtml you don't even need to specify its name when calling the EditorFor helper:
#model Hello
...
#Html.EditorFor(x => x.World)
By convention all properties of type string will use your template.

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.

MVC Partial View to Create/List Items and Redirect

What I'm trying to achieve is essentially have a view that at the top displays a form to enter new post, then below the form it will display a list of posts that already exist. Also I'm trying to make this view somewhat generic so that it can show different type of posts.
To be clear, by post I mean something like a facebook status post and not a Http-Post.
Imagine you have home page that displays all the posts, and then you have a profile page that displays only the posts connected to that profile.
So I have my _Posts partial view which looks like this:
#model IEnumerable<Post>
#Html.Partial("_CreatePost", new Post())
#foreach (var item in Model)
{
<div class="Post">
<h5>#item.Title</h5>
<p>#DateTime.Now.Subtract(item.Date).ToString("mm") minutes ago</p>
<p>#item.Content</p>
</div>
}
The code for the partial view is:
#model MVC_Test_CRUD.Models.Post
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm("Create","Post")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Post</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Content)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
And I successfully display a list of posts and I'm able to create new posts as well.
I also have a standard Create Action in my controller, which results in
return RedirectToAction("Index")
if the creation was successful. So my question is how can I send additional parameter to the controller about where to redirect (home/index or profile/index or somewhere else) depending on where
#Html.Partial("_CreatePost", new Post())
was inserted.

How can I programmatically trigger client side validation set up by a Razor view?

I have a little Ajax application where I use Razor views to initially generated HTML form segments that I later read and write from with knockout.js. Although I am doing no non-Ajax action requests, I use Razor to generate the HTML so I enjoy automatic generation of jQuery Validation attributes. E.g. in my single page, I render a hidden form like this:
<section id="person-detail">
#Html.Action("EditPartial", "Person")
</section>
The EditPartial action returns a partial view that looks a lot like this:
#using (Html.BeginForm())
{
<fieldset>
#Html.HiddenFor(model => model.Id, new { data_bind = "value: id" })
<div class="editor-label">
#Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.FirstName, new { data_bind = "value: firstName" })
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<p>
Update
Delete
</p>
</fieldset>
}
Because I'm never actually posting the form, and due to some unknowns, despite all properties on my Person model being marked with the Required attribute, I see no sign of client side validation. What must I do to trigger this validation when my save button is clicked?
suppose your form has a class 'main':
$('form').submit(function() {
var $form = $('form.main');
$form.valid();
});

Resources