Partial editor to show single property of model - asp.net-mvc

I have my model as follows
public class PlaceOrder
{
public int orderCode { set; get; }
public string Order_ID { set; get; }
public int orderDetailCode { set; get; }
[Required]
public string Topic { set; get; }
//50 more fields are there
}
Using editorforModel displays all the fields in the model. I want to have a editor helper which takes the property name and only shows editor for that specific property.
I wrote a create/edit/details actions for my model and working fine. What my final goals is that I want to have edit button next to every field on the details view. As soon I click on edit it allows to update and validate the input as well
EDIT
I am using following snippet for edit link
#(Html.Awe().PopupFormActionLink()
.LinkText("Edit")
.Name("editP")
.Url(Url.Action("PropertyEdit", "PlaceOrder", new
{
PropertyName = Html.NameFor(model => model.SubjectCategoryCode),
propertyValue = Html.IdFor(model => model.SubjectCategoryCode),
ordercode = Model.orderCode
})
)
.Title("Editor for " + Html.NameFor(model => model.SubjectCategoryCode))
and I want something that I pass the field name and it dispalys the relevant fields and do the validation

You could just use an EditorFor and a form for each field:
#using Html.BeginForm("action", "controller")
{
#Html.EditorFor(m => m.ordercode)
<input type="submit" />
}
#using Html.BeginForm("action", "controller")
{
#Html.EditorFor(m => m.orderDetailCode)
<input type="submit" />
}
Of course, you would need a different action for each item and you need a way to get the other values as well, since you're only posting one value to the controller. To achieve this you could include a hidden field with the id and retrieve the other values on the server.

There's the Html.EditorFor(m => m.Property) method for this (your model should be set to PlaceOrder to use this helper, as with any statically typed helpers).
Edit: Bah, Kenneth was faster :-).

Related

Sending up a list of objects on a create form

I have a view specific model that combines a few of my objects together but I was having a few issues with a list of objects.
My model is so:
public class RouteSubcontract
{
public RoutingSubcontracts Subcontracts { get; set; }
public RoutingPhases Phases { get; set; }
public List<RoutingApprovals> Approvals { get; set; }
}
I have my create form and everything is working correctly, I am using html helpers like so:
#Html.EditorFor(model => model.Subcontracts.subRevNbr, new { htmlAttributes = new { #class = "textReplace", #id = "frmRevNbr" } })
But the problem is when I need to have the list of approvals, I am not even sure how to start with this. I need the ability to create a list of items, I can with jquery have a button that then creates the row of textboxes I need to enter the data, but I am unsure how to ID or name them so that they are picked up correctly by my post back.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AdminRoutingCreate(RouteSubcontract rs)
For collections the modelbinder expects inputs named in the following format: ListProperty[Index].Property. For example, if you wanted to edit a property on RoutingApprovals named Foo, you're need something like:
<input type="text" name="Approvals[0].Foo" />
<input type="text" name="Approvals[1].Foo" />
<input type="text" name="Approvals[2].Foo" />
On post back you'd end up with three RoutingApprovals instances.

MVC 4 create a form with a list as a property of my new object

I'm creating a form for register new users, each user is allowed to have many address. (many address to one user). I found this article but doesn't look right for me, because I have a main property which is user and the address has to be related with it. I would like to create something similar for add many address even in the create screen for new user, which doesn't exist the user primary key for these address be related.
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Could someone send an example how is the better way to do it?
this is something very close to my code:
public class Person{
public int PersonId { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address{
public int AddressId { get; set; }
public string Street { get; set; }
[ScriptIgnore]
public virtual Person Person { get; set; }
}
As Klors indicated, create a view models for Address and Person (suggest List<AddressVM> is initialized in PersonVM constructor). In your PersonController
[HttpGet]
public ActionResult Create()
{
PersonVM model = new PersonVM();
model.Addresses.Add(new AddressVM()); // add an empty address assuming at least one required
return View(model, "Edit")
}
[HttpGet]
public ActionResult Edit(int ID)
{
PersonVM model = // get from database/repository
// If Address.Count == 0, add new Address
return View(model);
}
[HttpPost]
public ActionResult Edit(PersonVM model)
{
...
In your view
#model PersonVM
#using (Html.BeginForm("Edit", "Person") {
....
#Html.HiddenFor(m => m.ID)
#Html.TextBoxFor(m => m.Name)
...
#Html.EditorFor(m => m.Addresses) // you may want to create an editor template for AddressVM
<button type="button" id="AddAddress">Add address</button>
The EditorFor() will generate HTML similar to this (note the indexer)
<input type="text" name="Address[0].Street" id="Address_0__Street" ..../>
<input type="text" name="Address[0].Suburb" id="Address_0__Suburb" ..../>
To dynamically add new addresses, you need to use JavaScript to generate similar HTML, but increment the indexer. If you use an editor template, you can wrap the Address controls in a container (say <div class="address">) which make them easy to select as per the script below
Script to dynamically add a new address
$('#AddAddress').click(function() {
var addresses = $('.address');
// Get the number of existing address
var count = addresses.length;
// Get the first address and clone it
var clone = addresses.first().clone();
// Update the index of the clone
clone.html($(clone).html().replace(/\[0\]/g, '[' + count + ']'));
clone.html($(clone).html().replace(/"_0__"/g, '_' + count + '__'));
// Add to the DOM
addresses.last().after(clone);
}
Note this will also clone the values from the first address, so you may want to reset some or all of them, for example (after its been added)
clone.find('input').val('');
If you're using #Html.ValidationMessageFor() methods, note that dynamically added elements will not be validated in the browser unless you parse the form. An example of how to do that can be found here: jquery.validate.unobtrusive not working with dynamic injected elements
Something like this might be closer to what you need. You'll need to imagine the checkboxes are collections of fields for entering your address.
However, if you create a ViewModel for Person that contains a list of ViewModels for Address, you can create strongly typed editor templates and display templates named the same as the ViewModels and they'll automatically be picked up if you use #Html.EditorFor and #Html.DisplayFor which makes working with one to many's easier.
If you had files like so -
~/Models/PersonViewModel.cs
~/Models/AddressViewModel.cs
~/Views/Person/Edit.cshtml
~/Views/Shared/DisplayTemplates/AddressViewModel.cshtml
~/Views/Shared/EditorTemplates/AddressViewModel.cshtml
and a person ViewModel a bit like
public class PersonViewModel
{
public int Id { get; set; }
public string Name { get; set; }
...
public List<AddressViewModel> Addresses { get; set; }
}
If you then have an edit view for person like
#model PersonViewModel
<div>
#using (Html.BeginForm("Edit", "Person", new {id = Model.Id}, FormMethod.Post))
{
<div>
#Html.EditorForModel()
</div>
<div>
<p>#Html.DisplayNameFor(p => p.Addresses)</p>
#Html.EditorFor(p => p.Addresses)
</div>
<p>
<input type="submit" value="Save"/>
</p>
}
</div>
then the editor template should get picked up for the AddressViewModel once for each entry in the list. You'll have to add in some Ajax to allow new addresses to be created like in your example link. As long as the template contains all the fields for the AddressViewModel to work, then your Edit POST controller should just receive a PersonViewModel back as it's parameter.
There are some parts missing from my example, but you should be able to fill in the blanks from tutorials.

Get dynamic radio buttons values

I'm doing survey system on asp.net mvc 4. How can i get all radio buttons data in Controller ?
Example
$(document).ready(function(){
var AnswerCounter = 0;
$("#answer-add").click(function(){
var answertext = $("#answer-text").val();
var answerContent = $('<p>', {
class:'external',
});
var inputAnswer = $('<input>',{
id:'answer-'+AnswerCounter,
name:'rb',
class:'answer-radio'
}).attr("type","radio").appendTo(answerContent);
var labelAnswer = $('<label>').attr('for','answer-'+AnswerCounter).text(answertext).appendTo(answerContent);
$('#answers').append(answerContent);
AnswerCounter++;
});
});
You can see in the example buttons created by jquery. so i can use model binding.
Thanks.
The HTML typically geneated for MVC3 radiobuttons look like this
<input name="FeedingTime" id="FeedingTime" type="radio" value="Morning"/>
<input name="FeedingTime" id="FeedingTime" type="radio" value="Afternoon" checked="checked"/>
and when the radio button group posts, it will bind to the variable that matches the name.
[HttpPost]
public ActionResult Index (string FeedingTime){
//feeding time here is "Afternoon"
}
so to make your code bind correctly,
set the value of the input 'value' attribute in your script, and
have a variable in the ActionResult that matches the 'name' attribute in the html input element
EDIT: This answer is no longer relevant to the question being asked.
to get the value from radio buttons you can bind to them like you would a string variable; suppose in your view you have
#Html.LabelFor(m => m.FeedingTime,"Morning")
#Html.RadioButtonFor(m => m.FeedingTime, "Morning")<br />
#Html.LabelFor(m => m.FeedingTime,"Afternoon")
#Html.RadioButtonFor(m => m.FeedingTime, "Afternoon")<br />
#Html.LabelFor(m => m.FeedingTime,"Night")
#Html.RadioButtonFor(m => m.FeedingTime, "Night")
when you post back, you would capture this in a string variable
public ActionResult(..., string FeedingTime, ...){
}
normally this variable is embedded in a viewmodel such as
public class AnimalViewModel
{
public string Name { get; set; }
public string Type { get; set; }
public string FavoriteColor { get; set; }
public string FeedingTime { get; set; }
}
so that when you post
[HttpPost]
public ActionResult Index(AnimalViewModel viewModel){
...
}
it binds the viewmodel to the data automagically.
To answer your question more directly. I dont think theres a way to detect all radiobutton inputs on a page, you can only detect the results from that are posted from the radio button. There is no indication that the string posted was once the answer to a radiobutton. So, if you want to know the values of all radiobuttons on the page, you will simply have to check the values of the strings which are posted.

Mapping a list of string key-value pairs with Razor / form post

I have some HTML in my Razor view that is basically 3 rows of a pair of Select and Input elements:
(There could be more than 3)
<select name="SocialNetwork[1]">
<option>Skype</option>
<option>Twitter</option>
<option>Facebook</option>
</select>
<input name="SocialNetworkUsername[1]" type="text" />
<select name="SocialNetwork[2]">
<option>Skype</option>
<option>Twitter</option>
<option>Facebook</option>
</select>
<input name="SocialNetworkUsername[2]" type="text" />
<select name="SocialNetwork[3]">
<option>Skype</option>
<option>Twitter</option>
<option>Facebook</option>
</select>
<input name="SocialNetworkUsername[3]" type="text" />
When the form is posted to my controller method:
public ActionResult SaveDetails(MyModel model)
{
}
How can I 'map' the values of SocialNetwork | SocialNetworkUsername to a model?
Something like:
public class MyModel
{
public string SomeProperty { get; set; }
public string SomeOtherProperty { get; set; }
public Dictionary<string,string> SocialNetworks { get; set; }
}
I'm not 'stuck' using a dictionary if there is something more appropriate
You can use an editor template to achieve this.
the editor template will allow you to loop through a list of social networks in your main view and use the #Html.EditorFor helper to render out your dropdown lists and make model binding easy.
To start with your model should represent a single Social Network
In your model you need properties to contain the dropdown list values, and a property for the selected value. You'll also need a property to contain the SocialNetworkUsername
public IEnumerable<SelectListItem> SocialNetworks { get; set; }
public string SelectedSocialNetwork { get; set; }
public string SocialNetworkUserName { get; set; }
To make your editor template add a new folder /Views/Shared/EditorTemplates
In the EditorTemplates folder create a new view with the name MyClass (or whatever your model class name is)
You need to follow these naming conventions as there is no other configuration required for the editor template to work.
In MyClass.cshtml you need to allow editing of a single social network
#model MyModel
#Html.DropDownListFor(m => m.SelectedSocialNetwork, Model.SocialNetworks)
#Html.TextBoxFor(m => m.SocialNetworkUserName)
Then in your main view you use a List of MyModel as the model for the view and use a for loop to render out your editor templates.
#model List<MyModel>
#using(Html.BeginForm())
{
for (var i = 0; i < Model.Count; i++)
{
Html.EditorFor(Model[i])
}
}
In your controller just change your action method parameter to accept a List of MyClass and model binding should just magically work ;)

ASP.NET MVC 3 How to have multi-field create capability for Model with ICollection Property on Create view

Note: I'm using MVC3+Razor, EF4, CF-CTP5
How can you allow the view to have the ability to add multiple Address classes per Organization dynamically on the client, and bound strongly to the model on post?
How can you have the view parse values in the model if the (ModelState.IsValid == false) such that if you enter 3 addresses and post an invalid model, it re-populates the number addresses and with their appropriate values?
Here are my models:
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
...
}
public class Address
{
public int Id { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public int Type { get; set; }
}
I'm trying to figure out how you can have the Create action for Organization (/Organization/Create) handle the create like thus (such that addresses and phone numbers are part of the submitted model):
[HttpPost]
public ActionResult Create(Organization organization)
{
if (ModelState.IsValid)
{
_db.Organizations.Add(organization);
_db.SaveChanges();
return RedirectToAction("Details", organization.Id);
}
return View(organization);
}
Your question is quite vaste :)
This is just one of the way your requirement can be achieved and I am sure there are better than mine.
I am going to start from your second question:
How can you have the view parse values
in the model if the
(ModelState.IsValid == false) such
that if you enter 3 addresses and post
an invalid model, it re-populates the
number addresses and with their
appropriate values?
If I correctly understand your request it looks very simple to me. The answer is simply code your view to render a Model class content and return the invalid model to the client exactly as you are doing in your Create action.
If your form (and its fields) have been decorated with the ValidationSummary/ValidationMessage html helpers, you are going to see also validation messages.
How can you allow the view to have the ability to add multiple Address
classes per Organization dynamically
on the client, and bound strongly to
the model on post?
You can have a main view showing Organization attributes and then have another view showing related addresses. Here you can place a hyperlink or a button that open a dialog for adding a new address object and then refresh the address list when done. At the same way you can have edit and delete buttons as icons on the list.
The address list is a piece of markup completely handled at client side that, to be correctly binded to the server side Model class should adhere to some simple naming rules for it's input attributes.
To make the Default Model Binder class bind correctly your form use the following snippet for your Organization class
#using (Html.BeginForm()) {
#Html.HiddenFor(o => o.Id)
#Html.ValidationSummary( true )
<fieldset>
<legend>My Organization</legend>
<div class="editor-label">#Html.LabelFor( model => model.Name )</div>
<div class="editor-field">
#Html.EditorFor( model => model.Name )
#Html.ValidationMessageFor( model => model.Name )
</div>
<br />
<div id="container">
<div>Address List</div>
#foreach (Address a in Model.Addresses ) {
Html.EditorFor(a);
}
</div>
<div style="text-align:right;margin-top:14px;">
<input type="submit" id="btnSubmit" value="Save" />
</div>
</fieldset>
}
To be automatically bindable the resultant code for the form should look as the following
<form action="..." id="..." method="post">
<input type="hidden" name="Id" value="2">
<input type="hidden" name="Name" value="Acme Corporation">
<!-- markup for each address -->
<input type="hidden" name="Addresses[0].Id" value="1">
<input type="hidden" name="Addresses[0].Line1" value="Line 1">
<input type="hidden" name="Addresses[0].Line2" value="Line 2">
... and so on...
</form>
having it's properties named as Addresses[index].PropertyName.
If you add new addresses on the client it does'nt matter so much: as long as your code respect this rule you can have the default Model Binder do the job for you.
Hope this helps
I'm not sure if I understand your question correctly but with respect to question 1 I think you are looking for a ViewModel. Like this perhaps..
OrganizationViewModel.cs
public class OrganizationViewModel
{
public OrganizationViewModel(Organization org, IList<Address> addresses)
{
this.Organization = org;
this.Addresses = addresses
}
public Organization Organization {get;set;}
public IList<Address> Addresses {get;set;}
}
OrganizationController.cs
public class OrganizationController : Controller
{
private readonly IOrganizationService _organizationService: //or whatever method you use
public OrganizationController(IOrganizationService orgService)
{
this._organizationService = orgService;
}
public ActionResult Item(int id)
{
var org = _organizationService.GetOrganizationById(id);
var addresses = _organizationService.GetOrgAddressesByOrgId(id);
return View(new OrganizationViewModel(program, addresses));
}
}
Item.cshtml
#model OrganizationViewModel
<h1>#Model.Organization.Name</h1>
<ul>
#foreach(var a in Model.Addresses)
{
<li>#a.Line1</li>
<li>#a.Line2</li>}
</ul>
Before I try and answer number 2 maybe you should indicate whether I am correctly understanding question 1. Hope this helps.
I managed to do this using LINQ to SQL. Now I'm trying to use Entity Framework instead, but it really makes everything more complicated. So I don't have a solution for you, but perhaps my L2S solution might help?
Using models generated from my database I could in my view do this:
#for (int i = 0; i < Model.Contact.EmailAddresses.Count; ++i)
{
<li>
#Html.TextBoxFor(x => x.Contact.EmailAddresses[i].EmailAddress)
#Html.HiddenFor(x => x.Contact.EmailAddresses[i].EmailAddressID)
</li>
}
I had a view model class:
class ContactViewModel
{
Contact contact { get; set; }
}
This worked fine and in my controller action I got my Contact object with it's Contact.ContactEmailAddresses list filled just like I expected.
But with EF, I cannot use the [i] on the EmailAddresses property generated from the database anymore. The best I have come up with is:
#Html.TextBox("Contact.EmailAddresses[" + i + "].EmailAddress", Model.Contact.EmailAddresses.ElementAt(i).EmailAddress)

Resources