Two the same models in one view MVC - c#

I am having one form in my website. The form contains information about two localizations. First localization called FROM and second called TO.
In my view I am using
#model DDP.Models.Localization
Here is my Localization entity:
public class Localization
{
[Key]
public int ID { get; set; }
[Required]
public string Province { get; set; }
[Required]
public string City { get; set; }
[Required]
public string PostalCode { get; set; }
public string StreetAdres { get; set; }
}
The problem is I want pass to my controller two instances of Localization like below:
public ActionResult AddRoute(Localization from, Localization to)
{
return View();
}
There is problem when I want to do it like below because I have two the same models.
#Html.TextBoxFor(model => model.City, new { #id = "cityFrom", #class = "form-control", #placeholder = "Miasto", #style = "margin-bottom: 10px;" })
Can somone show me right direction?
Thanks!

Make a viewModel hosting the two instances, and use that into your view.
namespace DDP.ViewModels
{
public class vm
{
Localization L1 {get; set;}
Localization L2 {get; set;}
}
}
and use it into your view like this:
public ActionResult AddRoute(Localization from, Localization to)
{
DDP.ViewModels.vm ret = new DDP.ViewModels.vm() { L1 = from, L2 = to};
return View(ret);
}
#model DDP.ViewModels.vm
#Html.TextBoxFor(model => model.L1.City, new { #id = "cityFrom", #class = "form-control", #placeholder = "Miasto", #style = "margin-bottom: 10px;" })
(I didn't actually test the code, I just typed it here into the editor maybe there's some mistake but hopefully it should put you onto the right way!)

Related

Episerver block for login

I am new to episerver cms, I need to insert "login block" on top right corner of my home page and Front-end user should be able to login to the application. So I have created “LoginBlock”, ”LoginBlockController” and a class “LoginFormPostbackData”. Then I inserted the block on my page. Please find the code below
[ContentType(DisplayName = "LoginBlock", GUID = "42103d00-abbe-44e8-bfc2-c07a543cac86", Description = "")]
public class LoginBlock : BlockData
{
[CultureSpecific]
[Display(
Name = "Heading",
Description = "Add a heading.",
GroupName = SystemTabNames.Content,
Order = 1)]
public virtual String Heading { get; set; }
[Display(
Name = "LoginForm", Description = "Login Form",
GroupName = SystemTabNames.Content,
Order = 2)]
public virtual LoginFormPostbackData LoginPostbackData { get; set; } = new LoginFormPostbackData();
}
public class LoginBlockController : BlockController<LoginBlock>
{
public override ActionResult Index(LoginBlock currentBlock)
{
return PartialView(currentBlock);
}
}
public class LoginFormPostbackData
{
public string Username { get; set; }
public string Password { get; set; }
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
}
Please find the partial view of the block.
<div>
<h2>#Html.PropertyFor(x => x.Heading)</h2>
#Html.LabelFor(m => m.LoginPostbackData.Username, new { #class = "sr-only" })
#Html.TextBoxFor(m => m.LoginPostbackData.Username, new { #class = "form-control", autofocus = "autofocus" })
#Html.LabelFor(m => m.LoginPostbackData.Password, new { #class = "sr-only" })
#Html.PasswordFor(m => m.LoginPostbackData.Password, new { #class = "form-control" })
</div>
When I Try to run this application I am getting below error
Type 'EpiserverSite1.Models.ViewModels.LoginFormPostbackData' could not be mapped to a PropertyDefinitionType
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
Exception Details: EPiServer.Core.TypeMismatchException: Type 'EpiserverSite1.Models.ViewModels.LoginFormPostbackData' could not be mapped to a PropertyDefinitionType
Can any one help me to solve this issue?
LoginFormPostbackData isn't an Episerver property type. That is why it's complaining, it's essentially saying LoginFormPostbackData cannot be mapped to an Episerver property type.
You don't need a property for the form data, though. You could however use LoginFormPostbackData as a view model for your partial view, and possibly as a parameter type for your login controller to make it easier to receive the data from the form.

ASP.NET MVC Form POST is very slow for large nested checkbox list

I'm having a problem with my ASP.NET MVC web application where it takes ~30 seconds for my ViewModel to hit my controller when the form is posted. I'm guessing it has to do with the default Model Binder because of this.
[HttpPost]
public ActionResult Edit(ByActivityEditViewModel viewModel)
{
if (ModelState.IsValid) // Takes ~30 seconds before even hitting this
{
My View is a series of nested checkboxes of Group parents and User children. The same User may be listed under multiple Groups. I'm using an EditorFor to generate the checkboxes for the ViewModel.
View's Editor Template Call: #Html.EditorFor(model => model.Groups)
Editor Template:
#model MyProject.Models.Group
#Html.HiddenFor(model => model.Guid)
#Html.HiddenFor(model => model.Name)
#Html.CheckBoxFor(model => model.IsAllowed, new { #class = Model.Guid.ToString(), #style = "margin-right:5px; cursor:pointer;" }) #Html.LabelFor(model => model.IsAllowed, Model.Name, new { #class = "build-checkbox-label", #style = "font-weight:normal; margin-top:-2px;" })
#if (Model.Users.Any())
{
<ul style="list-style:none;">
#for (int i = 0; i < Model.Users.Count; i++)
{
<li>
#Html.HiddenFor(model => Model.Users[i].Guid)
#Html.HiddenFor(model => Model.Users[i].Name)
#Html.CheckBoxFor(model => Model.Users[i].IsAllowed, new { #class = Model.Users[i].Guid.ToString(), #style = "margin-right:5px; cursor:pointer;" }) #Html.LabelFor(model => Model.Users[i].IsAllowed, Model.Users[i].Name, new { #class = "build-checkbox-label", #style = "font-weight:normal; margin-top:-2px;" })
</li>
}
</ul>
}
ViewModel:
public class ByActivityEditViewModel
{
public int ActivityId { get; set; }
public string Path { get; set; }
public IList<Group> Groups { get; set; } = new List<Group>();
}
public class Group
{
public Guid? Guid { get; set; }
public string Name { get; set; }
public string DistinguishedName { get; set; }
public string SamAccountName { get; set; }
public string DisplayName { get; set; }
public bool IsAllowed { get; set; }
public List<User> Users { get; set; } = new List<User>();
}
public class User
{
public Guid? Guid { get; set; }
public string Name { get; set; }
public string DistinguishedName { get; set; }
public string SamAccountName { get; set; }
public string DisplayName { get; set; }
public bool IsAllowed { get; set; }
public bool IsUserChecked { get; set; }
}
ModelState:
The Model State contains the Guids, Names, and IsAllowed values. I believe processing this is the slow part.
There ends up being:
64 Groups
853 Users
The user can be part of more than one group.
I've tried:
Using string for Guid and Parsing it later.
Removing BeginCollectionItem package I was using.
Yelling at my monitor.
Any suggestions, information, or a workaround would be appreciated.
Jquery/js functions have done this for me previously a couple times, when handling large number of controls and i have banged my head trying to figure it out. Now I know where to look first.
So it is not clear from the post what is the fix unless you read the comments. So the answer is to add #{ Html.EnableClientValidation(false); } before #using (Html.BeginForm())
This turns off default client side validation and makes you form post faster to the server. Ofcourse if you need client side validation, then this is not the solution. In my case, my form had 200 rows with checkboxes and I didn't need the client side validation. So this solution worked great for me.

Passing multiple class objects to razor view through controller in mvc, asp.net

I am trying to perform CRUD operations on two classes PurchaseDetail and ItemDetail. Made PurchaseDetail a base class and ItemDetail as child class. So on a single razor view I am passing the child class object. Now when I'm performing CRUD operation on child class everything works fine. But when trying the same with base class I'm getting null object while adding data by calling a create(ItemDetail obj), on HttpPost.
Any solution for this? Is it a good practice to pass multiple classes to a view by using inheritance?
Edit
Elaboration:
public class PurchaseDetail
{
public int ID { get; set; }
[Required]
[Display(Name = "PPRF Name")]
public string pprfName { get; set; }
public int DepartmentID { get; set; }
[Display(Name ="Department")]
public string DepartmentName { get; set; }
public int DepartmentSerial { get; set; }
public List<SelectListItem> MaterialTypes { get; set; }
Above class is my base class with few properties. Then Below is my child class.
public class ItemDetail: PurchaseDetail
{
public int ItemID { get; set; }
public int ItemCategoryID { get; set; }
public string ItemCategoryName { get; set; }
public int ProductID { get; set; }
If I would take both in different pages there was not any problem. But I want to pass both class onto same view so I went for Inheritance. I am popping up form for ItemDetail from the same view, to add an item.
#model AIIMSINTRANET.Models.ItemDetail
Firstly I am inserting ItemDetail by using Ajax call. Here things are working fine.
public JsonResult Add(ItemDetail item)
{
//Insert Logic
}
After this I want to insert PurchaseDetail in it's respective table. Since the view was same so every fields like TextBox, Dropdowns were filled while calling this method.
public ActionResult Create()
{
ItemDetail obj = new ItemDetail();
obj.DeptIndentNo = GetIndentNumber2();
obj.IndentDate = DateTime.Now.Date;
obj.MaterialTypes = PurchaseProposalDO.PopulateMaterialType();
//Initialized all other fields
}
Even fields like DropDown of ItemDetail class got populated during above function call. Now when I am submitting the form to Insert PurchaseDetail, it calls below function:
[HttpPost]
public ActionResult Create(ItemDetail obj)
{
// Insert Logic Here
}
Now here is my problem. Why am I getting obj as null ?
Even when I am inputting everything.
<div class="form-group">
#Html.LabelFor(model =>model.pprfName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model =>model.pprfName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.pprfName, "", new { #class = "text-danger" })
</div>
</div>
Please help.
Without seeing the class definitions in question it's hard to answer definitively, but it sounds like what you've got is an Order Header/Order Line type relationship. If that's the case, using inheritance is absolutely the wrong approach for solving this. You should use a view model that encapsulates the other two objects and pass that. Something like:
public class OrderViewModel
{
public PurchaseDetail PurchaseDetail { get; set; }
public ItemDetail ItemDetail { get; set; }
}
Or, if there could be many ItemDetails for a single PurchaseDetail:
public class OrderViewModel
{
public PurchaseDetail PurchaseDetail { get; set; }
public IEnumerable<ItemDetail> ItemDetails { get; set; }
}
Alternatively, you could have the view model pass only the properties of interest to the view. Again, without seeing the definition of the classes it's hard to be more specific.

How to validate a unique property while user giving input (code first approach in .Net, MVC5)?

Model Class:
public class Course
{
[Key]
public int Id { get; set; }
[MinLength(5, ErrorMessage = "Code must be (5) characters long")]
[Index(IsUnique = true)]
[Column(TypeName = "VARCHAR")]
[Required]
[Display(Name = "Code")]
public string CourseCode { get; set; }
[Index(IsUnique = true)]
[Column(TypeName = "VARCHAR")]
[Required]
[Display(Name = "Name")]
[Remote("IsCourseNameExist", "Courses", HttpMethod = "POST", ErrorMessage = "Course is existed.")]
public string CourseName { get; set; }
//[System.ComponentModel.DataAnnotations.Compare("CourseName", ErrorMessage = "Already this Course is exist.")]
//[NotMapped]
//public string VeryfyName { get; set; }
[Range(0.5, 5, ErrorMessage = "Credit Must be between (0.5) to (5.0)")]
[Display(Name = "Credit")]
public decimal CourseCredit { get; set; }
public string Description { get; set; }
public int DepartmentId { get; set; }
public int SemesterId { get; set; }
[ForeignKey("DepartmentId")]
public virtual Department Department { get; set; }
[ForeignKey("SemesterId")]
public virtual Semester Semester { get; set; }
}
In Controller Class:
public JsonResult IsCourseNameExist(string CourseName)
{
//var course = .....
return Json(course == null);
}
In View:
<div class="form-group">
#Html.LabelFor(model => model.CourseName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CourseName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CourseName, "", new { #class = "text-danger" })
</div>
</div>
Description:
I am using Code First EF and very new in this area.
I have a Model class "Course" which generated a DB table named "Courses".
In view, user will add courses for a selected department and semester. But the Course Name property is unique.If user give a course name already exist in "Courses" table and submit button, then some error generates. That's why I want to make confirm that the user can not put any existing course name. So it needs checking before submission.
I have searched a lot, as I am newbie, everything is not clear to me. I found a way to use [Remote()] in model class and then use an action in controller to solve this. But cant apply it.
I expect some brief how to write the code that I mentioned in Controller class and what additional things need to add in view.
Thanks!
You can use a conditional statement combined with .Any() lambda expression.
public JsonResult IsCourseNameExist(string CourseName)
{
if(dbContext.Courses.Any(x => x.CourseName.Trim().ToUpper().Equals(CourseName.Trim().ToUpper())
{
return Json(false);
}
else
{
return Json(true);
}
}
Using .ToUpper() will help this be more efficient, because if your table you have a course name called Math 101.. and the user types in math 101 that might be submitted without error.
Let me know if this helps.

Display Decimal number asp.net MVC [duplicate]

The code below works fine but, in the textbox the decimal value has this format "0,0000"
(, is the decimal separator). I'd like have only 2 decimal. How can I do this ?
Thanks,
//Database model used with NHibernate
public class Bank
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName{ get; set; }
public virtual decimal Amount { get; set; }
}
//MVC Model
public class MyModel
{
public Bank Bank { get; set; }
}
//View
#Html.TextBoxFor(m => m.Bank.Amount, new { id = "tbAmount"})
Update 1
In the debugger, I don't see any decimal, wehn I do step by step inside (o #HTML.Textbofor) the view, the value does not have any decimal but when the page is displayed there are 4 decimals
//Database model used with NHibernate
public class Bank
{
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName{ get; set; }
public virtual decimal Amount { get; set; }
}
//Class for view
public class ViewBank
{
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
public decimal Amount { get; set; }
}
//MVC Model
public class MyModel
{
public Bank Bank { get; set; }
var ViewBank = new ViewBank() { Amount = Bank.Amount};
}
//View
#Html.TextBoxFor(m => m.Amount, new { id = "tbAmount"})
I would use editor templates and I would not use my NHibernate domain models in my views. I would define view models which are specifically tailored to the requirements of the given view (in this case limiting the amount to 2 decimals):
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
public decimal Amount { get; set; }
and then:
#Html.EditorFor(m => m.Bank.Amount)
This works for me
#Html.TextBox("Amount", String.Format("{0:0.00}", Model.Bank.Amount), new { id = "tbAmount"})
EDIT:
This is for TextBoxFor (does not work on MVC3)
#{var formated = String.Format("{0:0.00}", Model.Bank.Amount);}
#Html.TextBoxFor(m => m.Bank.Amount, formated, new { id = "tbAmount"})
In MVC 4 you can now pass the format as the second parameter
//View
#Html.TextBoxFor(m => m.Bank.Amount, "{0:n2}", new { id = "tbAmount"})
If you don't have customized editor template for Decimal type then EditorFor decorated with DisplayFormatAttribute would probably work out of the box.
For a custom editor template I ended up using something like:
#model decimal?
#{
string displayValue;
if (Model == null)
{
displayValue = null;
}
else {
var formatString = (ViewData.ModelMetadata).DisplayFormatString;
displayValue = formatString == null ? Model.ToString() : string.Format(formatString, Model);
}
}
<div class="form-group">
#Html.LabelFor(c => c)
#Html.TextBoxFor(c => c, new { type = "text", Value = displayValue, #class = "form-control" })
#Html.ValidationMessageFor(c => c)
</div>
Which works when the property is decorated with DisplayFormatAttribute like so:
[DisplayFormat(DataFormatString = "{0:n1}", ApplyFormatInEditMode = true), Display(Name = "Commission")]
public decimal? CommissionPercentage { get; set; }
This works for me.
in MVC5, in View:
#Html.TextBoxFor(o => o.Amount, "{0:n0}", new { #class = "form-control" })
Output before StringFormatting --> 15000.00
Output after StringFormatting --> 15000

Categories