Pass a model to a partial view? - c#

this is my partial:
#model RazorSharpBlog.Models.MarkdownTextAreaModel
<div class="wmd-panel">
<div id="wmd-button-bar-#Model.Name"></div>
#Html.TextAreaFor(m => m.Name, new { #id = "wmd-input-" + #Model.Name, #class = "wmd-input" })
</div>
<div class="wmd-panel-separator"></div>
<div id="wmd-preview-#Model.Name" class="wmd-panel wmd-preview"></div>
<div class="wmd-panel-separator"></div>
I'm trying to include it like this in my View:
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.Title)
#Html.TextBoxFor(m => m.Title)
#Html.Partial("MarkdownTextArea", new { Name = "content" })
<input type="submit" value="Post" />
}
these are the model classes:
public class MarkdownTextAreaModel
{
[Required]
public string Name { get; set; }
}
public class BlogContentModel
{
[Required]
[Display(Name = "Post Title")]
public string Title { get; set; }
[Required]
[DataType(DataType.MultilineText)]
[Display(Name = "Post Content")]
public string Content { get; set; }
}
What am I doing wrong, how should I do this in order to make my partial reusable?

Your partial expects an instance of the MarkdownTextAreaModel class. So do so, instead of passing an anonymous object which would throw anyways:
#Html.Partial("MarkdownTextArea", new MarkdownTextAreaModel { Name = "content" })
Now this being said a far better solution would be to adapt your view model, so that it contains a reference to MarkdownTextAreaModel and use editor templates instead of partials in your views, just like so:
public class BlogContentModel
{
[Required]
[Display(Name = "Post Title")]
public string Title { get; set; }
[Required]
[DataType(DataType.MultilineText)]
[Display(Name = "Post Content")]
public string Content { get; set; }
public MarkdownTextAreaModel MarkDown { get; set; }
}
then of course readapt the controller serving this view so that it populates the MarkDown of your view model:
public ActionResult Foo()
{
BlogContentModel model = .... fetch this model from somewhere (a repository?)
model.MarkDown = new MarkdownTextAreaModel
{
Name = "contect"
};
return View(model);
}
and then inside your main view simply:
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.Title)
#Html.TextBoxFor(m => m.Title)
#Html.EditorFor(x => x.MarkDown)
<input type="submit" value="Post" />
}
and then in order to follow standard conventions move your partial to ~/Views/YourControllerName/EditorTemplates/MarkdownTextAreaModel.cshtml and now everything will magically come into place as it should.

#using (Html.BeginForm()) {
#Html.LabelFor(m => m.Title) #Html.TextBoxFor(m => m.Title)
#Html.Partial("MarkdownTextArea", new MarkdownTextAreaModel { Name = "content" })
<input type="submit" value="Post" />
}

Related

Use two models in one form (view) - ASP.NET MVC

In my form, which I created in a view, the user can press add or search.
If the "add" button is pressed, a different model should be used in the background than with the "search" option. The add model is validated but otherwise does not differ from the search model.
By clicking "search" the user shouldn't be forced to fill in all fields.
Code
Model - AddModel
[Key]
public int Id { get; set; }
[Required]
[Display(Name = "Name")]
[StringLength(200, MinimumLength = 1, ErrorMessage = "Not Allowed")]
public string Name { get; set; }
[Required]
[Display(Name = "Place")]
[RegularExpression(#"^[\w ]*$", ErrorMessage = "Not Allowed")]
public string Place { get; set; }
Model - SearchModel
public int Id { get; set; }
public string Name { get; set; }
public string Place{ get; set; }
Controller
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Add(AddModel p) {
if (ModelState.IsValid) {
_ = InsertData(p);
ModelState.Clear();
return RedirectToAction("Add", new { Success = true });
}
return View();
}
public IActionResult Select(SearchModel p)
{
Task.WaitAll(SelectData(p));
return View(per); // per => list of selected data
}
View
#model **AddModel**
#if (ViewBag.success)
{
...
}
<form method="POST">
<div class="form-group">
#Html.LabelFor(m => m.Name, new { })
#Html.EditorFor(m => m.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Name, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Place, new { })
#Html.EditorFor(m => m.Place, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Place, "", new { #class = "text-danger" })
</div>
<input asp-action="Add" type="submit" class="btn btn-outline-primary" value="Add" />
<input asp-action="Select" type="submit" class="btn btn-outline-success" value="Search" />
</form>
The AddModel is still used in the View, but I would like to specify in the controller which model I would like to use. So if you press "search" the SearchModel and with "add" the AddModel should be used. I've already tried it with dynamic, but then it came to problems with the #html helpers.
Does somebody has any idea?
Would appreciate ;)
I think what you are looking to do is called a ViewModel, this should help : https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-3

mvc multiple models in view

I am making an MVC application. I am creating a View that uses a ViewModel and also use database entities in this view passed by the controller.
Controller:
public ActionResult AddGroup(AddGroupViewModel model)
{
ClassDeclarationsDBEntities1 entities=new ClassDeclarationsDBEntities1();
return View(entities.Subjects.ToList());
}
ViewModel:
public class AddGroupViewModel
{
[Required]
[Display(Name = "Subject")]
public string subject_name { get; set; }
[Required]
[Display(Name = "Number of Groups")]
public int qty { get; set; }
}
And finally my view:
#model List<ClassDeclarationsThsesis.Classes.Subject>
#model ClassDeclarationsThsesis.Models.AddGroupViewModel
#{
ViewBag.Title = "Add Groups";
}
<h2>Add Groups to subjects</h2>
#using (Html.BeginForm("AddGroup", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create new groups.</h4>
<hr />
#Html.ValidationSummary("", new { #class = "text-danger" })
<div class="form-group">
#{
List<SelectListItem> listItems1 = new List<SelectListItem>();
}
#foreach (var subject in Model)
{
listItems1.Add(new SelectListItem
{
Text = subject.name,
Value = subject.name,
Selected = true
});
}
#Html.LabelFor(m => m.subject_name, new {#class = "col-md-2 control-label"})
<div class="col-md-10">
#Html.DropDownListFor(m => m.subject_name, listItems1, new {#class = "form-control"})
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.qty, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.qty, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
As you see, I am trying to use two models in my view. But gives an exception (well how to distinguish them). How do I combine those two models in a view?
Edit:
So I did what suggested in answer, but now I get such exception:
{"The class 'ClassDeclarationsThsesis.Models.Subject' has no parameterless constructor."}
The class looks like this:
namespace ClassDeclarationsThsesis.Models
{
using System;
using System.Collections.Generic;
public partial class Subject
{
private int v;
private int userid;
public Subject(int v, int userid, string name)
{
this.class_id = v;
this.user_id = userid;
this.name = name;
}
public int class_id { get; set; }
public int user_id { get; set; }
public string name { get; set; }
public virtual Group Group { get; set; }
public virtual Subjects_Users Subjects_Users { get; set; }
public virtual Task Task { get; set; }
}
}
How do I solve it?
Since you already have a view model, I'd use that:
#model ClassDeclarationsThsesis.Models.AddGroupViewModel
And simply add a property to that view model for the collection you also want to use:
public class AddGroupViewModel
{
[Required]
[Display(Name = "Subject")]
public string subject_name { get; set; }
[Required]
[Display(Name = "Number of Groups")]
public int qty { get; set; }
public List<Subject> Subjects { get; set; }
}
Then simply create an instance of that from your controller to send to the view:
var entities = new ClassDeclarationsDBEntities1();
var model = new AddGroupViewModel();
model.Subjects = entities.Subjects.ToList();
// set your other properties too?
return View(model);
Then in the view simply refer to the property on the Model instead of the model itself when you need that collection:
#foreach (var subject in Model.Subjects)
Basically, while you can use only one type for your model (since there's only one Model property available to the view in the framework), that type can be anything you like, even a custom view model type that you define.

Passing two models to Controller using Ajax BeginForm()

I have View like this:
#model MVCApp.Models.User
#{
ViewBag.Title = "EditUser";
}
<h2>Edycja użytkownika</h2>
#using (Ajax.BeginForm("SaveUser", "My", new AjaxOptions { UpdateTargetId = "Result" }))
{
<fieldset>
<legend>Zmień dane użytkownika</legend>
<div id="EditUserForm">
<div>
#Html.LabelFor(m => Model.Login)
#Html.TextBoxFor(m => Model.Login)
</div>
<div>
#Html.LabelFor(m => Model.Password)
#Html.TextBoxFor(m => Model.Password)
</div>
<div>
#Html.LabelFor(m => Model.Name)
#Html.TextBoxFor(m => Model.Name)
</div>
<div>
#Html.LabelFor(m => Model.Surname)
#Html.TextBoxFor(m => Model.Surname)
</div>
<div>
#Html.LabelFor(m => Model.UserRole.Role)
#Html.TextBoxFor(m => Model.UserRole.Role)
</div>
<input type="submit" value="Zapisz zmiany" />
#Html.HiddenFor(m => Model.UserRole)
#Html.HiddenFor(m => Model.UserRoleID)
#Html.HiddenFor(m => Model.UserID)
</div>
</fieldset>
<div id="Result"></div>
#Html.ValidationSummary(true)
}
and method in MyController like this:
[HttpPost]
public ActionResult SaveUser(User user, UserRole role)
{
//code here
}
but object role is not passed, either user.UserRole.
My User model class:
namespace MVCApp.Models
{
public partial class User
{
public User()
{
this.Factures = new HashSet<Facture>();
this.Settings = new HashSet<Setting>();
this.Companies = new HashSet<Company>();
}
public int UserID { get; set; }
public Nullable<int> UserRoleID { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public virtual ICollection<Facture> Factures { get; set; }
public virtual ICollection<Setting> Settings { get; set; }
public virtual UserRole UserRole { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
}
And Role model class:
namespace MVCApp.Models
{
public partial class UserRole
{
public UserRole()
{
this.Users = new HashSet<User>();
}
public int UserRoleID { get; set; }
public string Role { get; set; }
public virtual ICollection<User> Users { get; set; }
}
}
so, How can I pass models like this, which has other reference types inside?
The following line in your view make no sense
#Html.HiddenFor(m => Model.UserRole)
UserRole is a complex object, and depending on whether you have overridden its .ToString() method, it will render <input ... value="MVCApp.Models.UserRole" /> so when this posts back the DefaultModelBinder is trying to do model.UserRole = "MVCApp.Models.UserRole" which of course fails and the property is therefore null
Remove it, and instead bind to the properties of UserRole that you want posted back - as you have done with #Html.TextBoxFor(m => Model.UserRole.Role). For example #Html.HiddenFor(m => Model.UserRole.UserRoleID) but you already seem to have bound this with #Html.HiddenFor(m => Model.UserRoleID) so it might not be necessay to repeat it.
you can create your own submitting mechanism. Just use $.ajax and pass in its data property all your values. A bit more js code, but a lot more flexibility.

How do I bind MVC POST names/values to controller ViewModel

I'm trying to POST a HTML form to my PersonController.AppSave() but I think there is a problem with the mapping/binding.
I am sending two values via POST { Person.id: '12345, XApplication.dateOfApplication: '01/01/2014'}, however my controller doesn't seem to directly recognise these values, as it is expecting XApplication (with good reason).
How do I bind these values? for example I want to bind Person.id to XApplication.personId.
Am I doing something fundamentally wrong?
Thanks for your help
#model MyNameSpace.ViewModels.StudentViewModel
#using (Html.BeginForm("Save", "Person", FormMethod.Post))
{
<div class="row">
<div class="col-md-12">
#Html.HiddenFor(m => m.Person.id, "default")
<div class="form-group">
<label for="dateOfApplication" class="col-md-3 control-label">Date of Application</label>
<div class="col-md-9">
#Html.EditorFor(m => m.XApplication.dateOfApplication, new { htmlAttributes = new { #class = "form-control datepicker" } })
</div>
</div><!-- form-group -->
</div><!-- .12 -->
</div><!-- .row -->
}
namespace MyNameSpace.ViewModels
{
public class PersonViewModel
{
public Person Person { get; set; }
public XApplication XApplications { get; set; }
}
}
namespace MyNameSpace.Controllers
{
public class PersonController : Controller
{
[HttpPost]
[Route("person/appSave")]
public ActionResult AppSave(XApplication application)
{
return Redirect("/person/app/" + application.personId);
}
}
}
namespace MyNameSpace.Models
{
public partial class XApplication
{
public int id { get; set; }
[Required]
public int personId { get; set; }
[Required]
[Column(TypeName = "date")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime dateOfApplication { get; set; }
}
}

Strange behaviour of ASP.NET MVC

I have the following model:
public class EditUserViewModel : AddEditUserViewModelPartial
{
public string OldUsername { get; set; }
public int UserId { get; set; }
public bool? NewPasswordGenerated { get; set; }
}
public class AddEditUserViewModelPartial
{
[Required]
[Display(Name = "Username/Email")]
public string UserName { get; set; }
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
....
public class AddEditUserPartial
{
public TypeOfForm Type { get; set; }
public AddEditUserViewModelPartial Model { get; set; }
}
public enum TypeOfForm
{
ADD,
EDIT
}
Partial Name:
#model CHFN.Models.AddEditUserPartial
<div class="form-group">
#Html.LabelFor(m => m.Model.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Model.UserName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Model.UserName)
</div>
</div>
....
Edit User:
#model CHFN.Models.EditUserViewModel
#Html.AntiForgeryToken()
<h4>Edit the account.</h4>
<hr />
#Html.HiddenFor(p => p.OldUsername)
#Html.HiddenFor(p => p.UserId)
#Html.HiddenFor(p => p.NewPasswordGenerated)
#Html.ValidationSummary(true)
#Html.Partial("PartialAddEditUser",
new CHFN.Models.AddEditUserPartial()
{
Type = CHFN.Core.TypeOfForm.EDIT,
Model = new CHFN.Models.AddEditUserViewModelPartial() { UserName = Model.UserName, Fullname = Model.Fullname, Roles = Model.Roles, Password = Model.Password, ConfirmPassword = Model.ConfirmPassword }
})
to EditUser (post) is went an empty OldUsername, UserId, NewPasswordGenerated. And, more surprised for me, if I remove Html.Partial("PartialAddEditUser",...) - then OldUsername, UserId, NewPasswordGenerated is not empty. Why it happens?
The problem seems to be that you are using different models for the main view and the partial view.
Consider the following line in your partial view:
#Html.LabelFor(m => m.Model.UserName, ...)
When you post the data back to an action that expect (I assume) EditUserViewModel as the input model, MVC will try to find a property named Model on EditUserViewModel (which is not there.), and set the UserName property on that.
The solution may be to simple not use the AddEditUserPartial class at all. The partial call may then look simply like this:
#Html.Partial("PartialAddEditUser", Model);
The partial view could then be simplified to the following (notice we use the exact same model, and thus get rid of the extra .Model):
#model CHFN.Models.EditUserViewModel
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.UserName)
</div>
</div>
....

Categories

Resources