I am developing an ASP.Net MVC 3 Web application and I recently posted a question about how to display a checkboxlist
ASP.Net MVC 3 Retrieve Checkbox List Values
and thanks to the help from RubbleFord and Darin Dimitrov I was able to get this working.
This works nicely for one checkboxlist, however, I now need to be able to display several checkboxlists on the same View, ie, see image attached.
The ViewModels I use to currently display one list are as follows;
public class ViewModelShiftSubSpecialties
{
public ListItem specialtyName { get; set; }
public IEnumerable<ViewModelCheckBox> SubSpecialityList { get; set; }
}
public class ViewModelCheckBox
{
public string Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
And within my Controller I populate ViewModelShiftSubSpecialties like so:
var subSpecialties = GetSubSpecialtiesForSpecialty(5);
ViewModelShiftSubSpecialties viewModel = new ViewModelShiftSubSpecialties();
var checkBoxList = new List<ViewModelCheckBox>();
viewModel.specialtyName = _listService.GetListItemByID(5); //Medicine Specialty
foreach (var item in subSpecialties)
{
ViewModelCheckBox chkBox = new ViewModelCheckBox { Id = item.subID.ToString(), Name = item.ListSub.description, Checked = false };
checkBoxList.Add(chkBox);
}
viewModel.SubSpecialityList = checkBoxList;
In my View, I display the list name and also use an Editor template to display the checkboxlist
<h3>#Model.specialtyName.description</h3>
#Html.EditorFor(m => m.SubSpecialityList)
However, I am totally stumped as how to get the above code to work with multiple checkboxlists on one View. Is this even possible?
I would really appreciate if someone could please help me with this.
Thanks.
It looks like you've done all the work already. You already have an Editor Template that works correctly with a ViewModelCheckBox IEnumerable. Editor templates wouldn't be useful if you couldn't reuse them for the same datatype. You just need to use it three times. Just extend your ViewModel
public class ViewModelShiftSubSpecialties
{
public ListItem specialtyName { get; set; } //Might need 3 of these
public IEnumerable<ViewModelCheckBox> SubSpecialityList1 { get; set; }
public IEnumerable<ViewModelCheckBox> SubSpecialityList2 { get; set; }
public IEnumerable<ViewModelCheckBox> SubSpecialityList3 { get; set; }
}
Create all three in your controller (and give them better names then I did).
And then in your View
#Html.EditorFor(m => m.SubSpecialityList1)
#Html.EditorFor(m => m.SubSpecialityList2)
#Html.EditorFor(m => m.SubSpecialityList3)
Alternatively you could create a class that contains a single specialty name and IEnumerable ViewModelCheckBox, and have your ViewModel have an IEnumerable of this new class. Then create a new Editor Template for this new class. I think this is worth it if your list size is variable/ might change. Otherwise I'd use the earlier solution for a simple fix.
public class ViewModelShiftSubSpecialties
{
public class IEnumerable<SubSpecialty> { get; set; }
}
public class SubSpecialty
{
public ListItem specialtyName { get; set; }
public IEnumerable<ViewModelCheckBox> SubSpecialityList
}
Related
I am working on creating a datagrid in ASP MVC 3 where I have two tables in one view. Each table is being used from its own Model. Therefore, I would have to call two Models into one View which does not seem as simple as I wish it was.
I am pretty new to MVC and I was looking through Stack and found this link:
Two models in one view in ASP MVC 3
Which seems to be the direction that i would want to go... I think.
Here is the code for my first model:
[Table]
public class Model1
{
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
public int Column1 { get; set; }
[Column]
public string Column2 { get; set; }
[Column]
public string Column3 { get; set; }
}
Here is the code for my second model:
[Table]
public class Model2
{
[Column(IsPrimaryKey = true, IsDbGenerated = true)]
public int Column1 { get; set; }
[Column]
public int Column2 { get; set; }
[Column]
public string Column3 { get; set; }
[Column]
public string Column4 { get; set; }
}
Here is the code for the Parent View Model that the other Stack Forum suggested:
public class ParentModelView
{
public Model1 Model1 { get; set; }
public Model2 Model2 { get; set; }
}
I was able to get each one to work individually so i know that it isn't any other issue. The other Stack Forum seemed to be a little too vague for my understanding. I feel like there would have to be more to it than just adding another parent model that is using each model within it (what i am getting out of it).
Some other information that you may find useful is that each model is in its own file. Also, Here is my error:
The model item passed into the dictionary is of type
'System.Data.Linq.Table 1[Model1]', but this dictionary requires a model
item of type 'System.Collections.Generic.IEnumerable 1[ParentModelView]'.
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: System.InvalidOperationException: The model item passed
into the dictionary is of type 'System.Data.Linq.Table 1[Model1]', but this
dictionary requires a model item of type
'System.Collections.Generic.IEnumerable 1[ParentModelView]'.
Source Error:
An unhandled exception was generated during the execution of the current
web request. Information regarding the origin and location of the exception
can be identified using the exception stack trace below.
---EDIT 1---
Here is the code inside of my View:
#model IEnumerable<ParentModelView>
#{
WebGrid gridModel1 = new WebGrid(Model);
WebGrid gridModel2 = new WebGrid(Model);
}
<div class="Table">
<h2>Model1</h2>
#gridModel1.GetHtml(columns: new[] {
gridModel1.Column("Column1"),
gridModel1.Column("Column2"),
gridModel1.Column("Column3")
})
</div>
<div class="Table" id="rightTable">
<h2>Model2</h2>
#*#gridModel2.GetHtml(columns: new[] {
gridModel2.Column("Column1"),
gridModel2.Column("Column2"),
gridModel2.Column("Column3"),
gridModel1.Column("Column4")
})*#
</div>
EDIT 2
As most of you have requested, here is my controller code. I know that it is not right because i am not quite sure how to pass through the information between two models into the same view. If someone would be able to help with that, it would be much appreciated.
public class HomeController : Controller
{
public ActionResult Index()
{
Model1Repo repoModel1 = new Model1Repo ();
var Model1RepoSQL = repoModel1.All();
Model2Repo repoModel2 = new Model2Repo();
var Model2RepoSQL = repoModel2.All();
return View(Model1RepoSQL);
}
}
Any other information would be much appreciated. Thanks!
I think what you want is something more like this:
public class ParentModelView
{
public IEnumerable<Model1> Model1 { get; set; }
public IEnumerable<Model2> Model2 { get; set; }
}
And in your view
#model ParentModelView
#{
WebGrid gridModel1 = new WebGrid(Model.Model1);
WebGrid gridModel2 = new WebGrid(Model.Model2);
}
EDIT;
Apparently, you aren't populating the parent model correctly. I assumed you would understand how to do that.
public ActionResult MyAction() {
var model1 = // get rows of model1
var model2 = // get rows of model2
return View("myview", new ParentModelView { Model1 = model1, Model2 = model2 }) ;
}
You can always use ViewModel in this case. For example create a viewmodel
public class ViewModel{
public int Table1Column1 { get; set; }
public string Table1Column2 { get; set; }
public string Table1Column3 { get; set; }
public int Table2Column1 { get; set; }
public int Table2Column2 { get; set; }
public string Table2Column3 { get; set; }
public string Table2Column4 { get; set; }
}
Get the data into ViewModel from both the models in business layer or data access layer.
P.S: What McGarnagle said is true but it's vice versa, you are sending child models into the view while it is expecting ParentModelView. If you can post parts of code for controller and view, it would be helpful.
I Want to display label for input box but I have 3 different class which contains information
Like Class1 [Settings] is like this
public class Settings
{
public static decimal Setting1 { get; set; }
public static decimal Setting2 { get; set; }
}
we are populating data in above class at the time of login, so we kept it in session
My Class2 [AppConst] is like this
public static class AppConst
{
public const string _unitPersentage = "%";
public const string _unitPerMT = "Per MT";
}
and my Modal is like this
public class Sale
{
[Display(Name = "Discount")]
public double DiscountAmount { get; set; }
[Display(Name = "Road Tax")]
public double RoadTaxAmount { get; set; }
}
Now in my View for Model "Sale" I want to display label for property "DiscountAmount" like this
Discount 5% (Lable of DiscountAmount + Value of Settings.Setting1 + value of AppConst._unitPersentage)
Here I can get label for "DiscountAmount" by using
#Html.TextBoxFor(o => o.DiscountAmount)
but how can I display value of tow other members "Settings.Setting1" & "AppConst._unitPersentage"
You can flatten out your model and then use an editor template to show that complex label.
The Flatten Model
public class CustomModel
{
[Display(Name = "Discount")]
public double DiscountAmount { get; set; }
[Display(Name = "Road Tax")]
public double RoadTaxAmount { get; set; }
public decimal Setting1 { get; set; }
public string UnitPercent {get;set;}
}
The Template
The template should be placed inside the EditorTemplates folder under the Shared folder.
#model CustomModel
#{
var inputProp = ViewData.ModelMetadata.Properties
.First(o => o.PropertyName == "DiscountAmount");
}
<label for="#inputProp.PropertyName" id="#(inputProp.PropertyName+"Label")">
Discount 5% (#inputProp.ShortDisplayName #Model.Setting1#Model.UnitPercent)
<input id="#inputProp.PropertyName" type="text"
name="#inputProp.PropertyName" />
</label>
Controller Code
This is how you will create and pass the model to your view:
return View(new CustomModel
{
// get it from your datastore or where ever the source may be
Setting1 = getFromDbOrSomewhere(),
UnitPercent = AppConst._unitPersentage,
});
Your View
Then consume it on your view like this
#model CustomModel
#Html.EditorFor(m => Model)
The beauty of this approach is that you can always discard the template and do inline html in your view. Also, the logic of having an "input + label" for your model is separated into one place.
You have few alternatives to try. If the data is only for display purpose you may use - ViewData or ViewBag or TempData to pass the values for Settings and AppConst, whatever suits your purpose. See here.
In this case, if these are static objects, you may refer them in the view as:
<Namespace>.Settings.Setting1 or <Namespace>.AppConst._unitPerMT.
Please take this as a starting point and follow proper best practices for development. Please do not declare public members with underscore. Underscores are generally prefixed to private members.
I'm having a trouble do the following mapping:
Domain (simplified version):
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual ModelDto Model { get; set; }
}
ViewModel:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
In the Controller, when I do the Mapping I'm loosing the Make_ID from the Dropdownlist of the View:
public virtual ActionResult Create(CreateAdViewModel adViewModel)
{
if (ModelState.IsValid)
{
var adDto = Mapper.Map<CreateAdViewModel, CreateAdDto>(adViewModel);
_adService.CreateAd(adDto);
}
return RedirectToAction(MVC.Home.Index());
}
The mapping is:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>()
Thanks.
As you have mentionned, the Ad need to know the Model_Id and to set it into the Model
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
You also need from the other mapping side to let the Dto know where to get the Model id.
Mapper.CreateMap<Ad, CreateAdDto>()
.ForMember(dest => dest.Model_Id, opt => opt.MapFrom(src => src.Model.Id}));
The code above is not secure because a validation to see if Model is null should be added.
For the rest of your code, you seem to do it right. The section with Entity Framework requires you to attach because the entity Model already exist, otherwise, EF would insert this entity to the database.
CreateAdDto doesn't have a Make navigation property or a Make_Id property.
Solution found after some research:
ViewDomain:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
public int Model_Id { get; set; }
// Navigation properties
//public virtual ModelDto Model { get; set; }
}
Domain:
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
Viewmodel -> Dto Mapping:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>();
Dto -> Domain Mapping:
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
Atention:
To achieve this with Entity Framework, I had to attach first the Model Entity to the Context and then Ad the new Ad:
public void CreateAd(CreateAdDto adDto)
{
var adDomain = Mapper.Map<CreateAdDto, Ad>(adDto);
_modelRepository.Attach(adDomain.Model);
_adRepository.Add(adDomain);
_adRepository.Save();
}
Hope this is the best practice.
BTW I would like to have some opinions regarding this aproach.
Thanks.
Based on what is I see in your question, I suggest a simple approach. Your application is medium scale. You should very carefully about maintainability,my experience say.So try to create a simple an strain forward approach for yourself like below approach:
I can describe all layer in detail but with notice to title of your question I prefer describe only Model(bussiness Ojbect) layer:
Good! As you can see PM.Model include:
Tow sub folders contain our ViewModels and in root of Library we have a .tt file contain Entity framework Objects (POCO classes) and we have a Mapper folder(Since that i dont like use autoMapper or third party like this :) ).
You can see IListBox interface in Domain layer. I put all ListBox container to this interface.
I hope current approach useful for you but finally I suggest remove one of this layers DTO or ViewModel, because in the future will be very complex.
Good luck
Do you aware about cost of these mapping?! You have 2 layers mapping (before arrived to Entity framework) for an simple insert.We can do more complex CRUD(s) in less than 2 layers mapping.
How to think about maintainability of this code?
Please keep DRY,KISS,SOLID conventions in your mind and top of your everyday work.
Good luck
Here is view models
public class ArticleViewModel
{
public string ID { get; set; }
public string Title{ get; set; }
public string Body { get; set; }
public List<BETag> TagsList { get; set; }
}
public class BETag
{
public string ID { get; set; }
public string Name { get; set; }
}
An action
[HttpPost, AuthorizeEx]
public ActionResult AddArticle(ArticleViewModel articleViewModel)
{
//articleViewModel.Tags.Count == 0
//Request.Form["TagsList"] == "tag1, tag2..."
}
and a part of AddArticle.cshtml
#Html.TextAreaFor(m => m.TagsList )
My question is why articleViewModel.Tags.Count is equal 0, but
Request.Form["TagsList"] is equal "tag1, tag2..."? How to bind ArticleViewModel properly?
Lists don't work that way in MVC. You need to use something like EditorFor(m => m.TagsList) and then you need to create a BETag EditorTemplate. But that's only part of the problem, and really won't work for you either.
What you really want is just a simple string that takes your list of tags, such as
public string TagListString {get;set;}
Then, in your controller, you parse the string and extract all your tags, then add them to the TagsList.
var tags = TagListString.Split(' '); // assumes only single space between tags,
// you should add more filtering to make sure
foreach(var tag in tags) {
TagList.Add(new BETag() { Name = tag });
}
MVC works with single items, not complex types. There is some built-in processing to breakdown complex types in some cases, and to automatically iterate over collections, but those don't work in your case because you want to edit all the items in a single field. So your only option is to parse the field in the post method and put the data where you want it.
I have been playing around with MVC 3 and looking at populating dropdownlists. I have seen a few examples online that recommend using view models, so here is my first attempt. My code seems to work, but can anybody tell me if this is the correct way to do this?
My model :
public class ContactGP
{
public int TeamID { get; set; }
[Required(ErrorMessage = "Please select a Team Name")]
[DataType(DataType.Text)]
[DisplayName("Team Name")]
public string TeamName { get; set; }
}
My view model :
public class ContactGPViewModel
{
public string SelectedTeamID { get; set; }
public IEnumerable<Team> Teams { get; set; }
}
My controller :
public IEnumerable<Team> PopulateTeamsDropDownList()
{
IEnumerable<Team> lstTeams = _Base.DataRepository.GetTeams();
return lstTeams;
}
public ActionResult ContactGP()
{
var model = new ContactGPViewModel
{
Teams = PopulateTeamsDropDownList()
};
return View(model);
}
And my view :
<p>
#Html.DropDownListFor(
x => x.SelectedTeamID,
new SelectList(Model.Teams, "TeamID", "TeamName")
)
</p>
Your code seems correct. You have defined a view model containing the necessary properties your view will require, filled it up in the controller and passed to this strongly typed view.
I have only a minor remark on the following line inside the PopulateTeamsDropDownList method:
_Base.DataRepository.GetTeams();
I hope you have abstracted this repository with interfaces (or abstract classes) and used DI in order to inject some concrete implementation into your controller. This will weaken the coupling between your controller and the way data is accessed and to simplify unit testing the different layers of your application in isolation.