This is my first question on this community, I hope someone can help.
I have 3 models: Parent, child and grandchild. Resource, Parameter and Metric. I create the controlers with entity framework and it generated all CRUD operations. My problem its basically the grandchild. When I go to create a new Metric (grandchild), I have the dropdownlist of all the Parameters (child), but I want first to choose the Resource, then that will list all the parameters from that resource so I can create the metric. I'm new on asp MVC and this might be a lil bit basic question but I couldn't find anything like this. Thanks
Here are my classes just in case.
public class Resource
{
public int ResourceID { get; set; }
public string Name { get; set; }
public virtual ICollection<Parameter> Parameters { get; set; }
}
public class Parameter
{
public int ParameterID { get; set; }
public string Name { get; set; }
public int ResourceID { get; set; }
public virtual Resource Resource { get; set; }
public virtual ICollection<Metric> Metrics { get; set; }
}
public class Metric
{
public int MetricID { get; set; }
public string Name { get; set; }
public int ParameterID { get; set; }
public virtual Parameter Parameter { get; set; }
}
What you are trying to do sounds like either a linked or cascading drop down list. I did a quick search and found this link that may help you.
You will need some JavaScript that listens to the selection changed event for the first list (parent), and then updates the second list (child) with the available values. Depending on your situation, you may then need to link the third list (grandchild) to load when the second list has its selection changed as well.
Related
I am fairly new to MVC in general but I am trying to figure out the best way to do create a form to create a model that has a list of another object as a property.
Here is my form on my web page:
Create Recipe Form
When you click the "Add another step" button it will add another section to the form to enter in the details of the next step.
Below are the models I am using with the annotations removed.
public class Recipe
{
public int id { get; set; }
public ApplicationUser Author { get; set; }
public string name { get; set; }
public string description { get; set; }
public int yield { get; set; }
public int preptime { get; set; }
public int cooktime { get; set; }
public List<RecipeVariation> variations { get; set; }
public List<RecipeStep> steps { get; set; }
public List<RecipeNote> notes { get; set; }
}
public class RecipeStep
{
public int id { get; set; }
public int stepNum { get; set; }
public string header { get; set; }
public string bodyText { get; set; }
}
My current solution is to pass a FormCollection through to the HttpPost ActionResult Create in my controller, however, the collection is difficult to parse through. Everytime you add a new "RecipeStep" the name is altered so that I do not have the values concatenated when calling Model.header or Model.bodyText. Is there a better way to parse through this information or even pass through the RecipeSteps as a List?
Thank you
First of all you need to take a foreign key receipeId in receipestep model.
Then on create button click in action method in controller take two parameters(Receipe receipe,List receipestep) and pass object to those parameters.
Then save object to database as first insert into Receipe table and then insert into receipestep table and pass Id of Receipe model as foreign key ReceipeId in ReceipeStep model.
Thanks.
Hope you understand
As a newbie with ASP.NET MVC pattern, I'm trying to create a small web application in order to practice knowledge learned through some tutorials.
I have some obscure things about Model and ViewModel. I understand that Model defined only the table structure whereas ViewModel defined the logic for data, How to handle data... Furthermore, ViewModel is used when I want to display more than one model into my View.
Well, it's very theoretical and I'm trying to develop this with my application.
My application:
This application lets to handle projects and people. I can create a new project object containing some properties: project name, project location, ... and add a list of people who will work on this specific project.
Then, in the other side, I have a simple people table with properties like : Firstname, Lastname and function.
When I create a new project, I would like to select one or multiple people in order to attach them to the project.
My class People:
I created a simple class which looks like this:
public class People
{
public int PeopleID{ get; set; }
public string Lastname { get; set; }
public string Firstname { get; set; }
public string Job{ get; set; }
}
I removed Annotations in order to see clearer my class.
I created the CRUD associated and it works fine.
My class Project:
In this class, I define properties from project object and I would like add a list of people collaborating on the project.
I have:
public class Project
{
public int ProjectID { get; set; }
public string ProjectName{ get; set; }
public string ProjectLocation{ get; set; }
public List<People> ListOfPeople { get; set; }
}
If I understand, now I have to create a ViewModel in order to create my Project object with information from Project and People classes ?
My class ProjectPeopleVM:
This class is identical as Project class ?
So I have:
public class ProjectPeopleVM
{
public int ProjectID { get; set; }
public string ProjectName{ get; set; }
public string ProjectLocation{ get; set; }
public List<People> ListOfPeople { get; set; }
}
Is it right ? I have some doubts about this.
If I want to create a controller class which let to Create a new object Project, I have to fill the ListOfPeople by using EF ? Something like db.People.ToList() ?
public class ProjectsController : Controller
{
private MyAppContext db = new MyAppContext();
public ActionResult Create()
{
var people_list = db.People.ToList();
var project = new ProjectPeopleVM
{
ListOfPeople = people_list;
// What I need to add here ? Data to populate ListOfPeople come from people_list variable
}
return View(project);
}
I'm a bit lost.
It's not necessary that you create a duplicate of your class just to see the information but it seems that in your code, you might need to because;
I noticed that in your Project Model, you didn't use public virtual List<People> or the virtual keyword, which is used for "lazy loading". When an existing project is taken from the db context, if you have the virtual keyword on your property, then it would automatically load the associated object. On the other hand, if you don't have it, then you will need to manually assign the list. Using lazy loading might have a little effect on the performance but with this you can view the properties of your child class immediately.
If your list has virtual property,
public class Project
{
public int ProjectID { get; set; }
public string ProjectName{ get; set; }
public string ProjectLocation{ get; set; }
public virtual List<People> ListOfPeople { get; set; }
}
Then in your controller or view, you could navigate to the properties of the objects in that list;
// this will give the first name of the first person on the first project
db.Project.FirstOrDefault().ListOfPeople.FirstOrDefault().FirstName;
For your create action in your controller, since it's a new Project, it doesn't have any People in it. Hence you will need to manually populate that list. To populate it, since you only need specific people, I suggest to use a checkbox or multiple input fields (aided with javascript).
The ListOfPeople can be populated by having a form element;
<input name="ListOfPeople[1].PeopleId" value="1"/>
<input name="ListOfPeople[1].FirstName" value="Mark"/>
<input name="ListOfPeople[1].LastName" value="Jacob"/>
<input name="ListOfPeople[1].PeopleId" value="2"/>
<input name="ListOfPeople[2].FirstName" value="Red"/>
<input name="ListOfPeople[2].LastName" value="Wandersee"/>
When you submit the form, the values will be bound to the Project model's ListOfPeople. Then that's the time you will need to loop through it and create a ProjectPerson (junction) record which determines where this person belongs to.
foreach(var i in model.ListOfPeople){
ProjectPerson pp = new ProjectPerson();
... // do property assignment
db.ProjectPerson.add(pp);
}
your view models should be used to map a combination of data drawn from the db. i.e.
public class Project
{
public int ProjectID { get; set; }
public string ProjectName{ get; set; }
public string ProjectLocation{ get; set; }
public IColleciton<People> ListOfPeople { get; set; }
}
public class People
{
public int Id{ get; set; }
public string Name{ get; set; }
public virtual Project project { get;set; }
}
this will allow relational retrieval from EF
viewModel should be used when you need a combination of this data without relationship i.e.
public class Project
{
public int ProjectID { get; set; }
public string ProjectName{ get; set; }
public string ProjectLocation{ get; set; }
}
public class People
{
public int Id{ get; set; }
public string Name{ get; set; }
}
public class ProjectVm
{
public int ProjectID { get; set; }
public string ProjectName{ get; set; }
public string ProjectLocation{ get; set; }
public ICollection<People> ListOfPeople { get; set; }
}
you can use a tool such as automapper to set up the mapping between objects which will generate custom model inside controller.
see this article: http://bengtbe.com/blog/2009/04/14/using-automapper-to-map-view-models-in-asp-net-mvc/
and DB Relationships
https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/conventions/built-in
I am trying to write mapping configuration for next case. I have domain object:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string ImagePath { get; set; }
public virtual ICollection<Service> Services { get; set; }
public int? SubcategoryId { get; set; }
[ForeignKey("SubcategoryId")]
public virtual Category Subcategory { get; set; }
}
And Dto to map:
public class CategoryDto
{
public int Id { get; set; }
public string Name { get; set; }
public string ImagePath { get; set; }
}
The problem is, target class have less properties, than source. If I use simple map, I get an exception.
Mapper.Initialize(n => n.CreateMap<Service, ServiceDto>());
I can't use Ignore(), because it will be applied to target class, not source one. Method ForSourceMember() also didn't help for some reason. I read this question, it's fine for most cases, but property Services is not null, it's Count = 0, when it's empty. I also read some similar questions from the right, but they didn't help, maybe they worked in previous versions.
Hope someone can help me to find solution, or explain what I missed.
Mapper.Initialize can only be called once, when your app initializes itself, not per request as you're doing now.
I'm working on a recommendation algorithm which all works fine. But now I wanted to implement this code into the branch of my development team.
I'll start from the top. My algorithm can recommend 2 types of objects, restaurants and dishes.
Restaurant:
public class Restaurant
{
public Guid Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
public List<Tag> Tags { get; set; } = new List<Tag>();
public int PriceRange { get; set; }
}
And dish:
public class Dish
{
public Guid Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public virtual Restaurant rest { get; set; }
[ForeignKey("rest")]
public Guid RestaurantId { get; set; }
public List<Tag> Tags { get; set; }
}
Now my product owner wants the list to be like this when it's being presented on the home page of our app:
[Restaurant][Dish][Restaurant][Dish] Etc...
So basically, he wants to alternate the type of object that's being recommended. These dishes and restaurants are completely separate. They are generated by my algorithm purely on the user's preferences and have no correlation with eachother at all.
Now my problem is how to return such a list. I figured I'd need a wrapper class which contains either a Restaurant or Dish like this:
public class RecommenderItem
{
public Restaurant rest { get; set; }
public Dish dish { get; set; }
}
This way I can create a List<RecommenderItem> and return that to the client. The client would only need to check which attribute is null and retrieve the values from the one that is not.
I'm just unsure if this is the correct approach. Are there any 'best practices' in doing this? Let me know if I should elaborate more!
If they doesn't have common base class then creating one wrapper class is the best solution. At the same time you can be more flexible and create something like
public class RecommendationItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public string PageUrl { get; set; }
public object Entity { get; set; }
}
So you can include all common information in this class and client will not be required to check with which object type he works. In such case it would be easier to add one more item type. At the same type I added reference to entity itself - it can be used if some specific handling for one or two item types is required.
You can declare an interface IRecommenderItem:
public interface IRecommenderItem
{
//shared properties
}
public class Restaurant : IRecommenderItem
{
}
public class Dish : IRecommenderItem
{
}
than, you can type:
List<IRecommenderItem> m = new List<IRecommenderItem>();
If you are going to connect pairs of elements it always makes sense to me to... well, pair the elements. I am assuming that each dish is specific to a particular restaurant? So the list would be [Restaurant1][Dish for Restaurant1][Restaurant2][Dish for Restaurant2]...?
I like the previous answer by oryol creating a common base class as well.
So, your RecommenderItem class is fine. But fill in both properties and pass a list of pairs back. Expand the list into the full set of items for display by creating a new List, iterating through the list of RecommenderItems and adding Restaurant and Dish from each entry in it.
Ok, I have 3 models. WorkoutViewModel has a one to many relationship with WorkoutExerciseViewModel. WorkoutExerciseViewModel has a one to many relationship with ExerciseSetViewModel. I need a dynamic “Create View”, that will allow me dynamically add Exercises to Workouts, and Sets to Exercises. I then want to save a Workout including all exercise and set records back to the database. I just need to validate that there is at least 1 exercise for the workout created and at least 1 set for the exercise created. Ultimately I just need to push a Workout View Model back to the controller with all of the populated nested IEnumberable objects present. Can anyone point me in the right direction?
public class WorkoutViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtal IEnumerable<WorkoutExerciseViewModel> WorkoutExercises { get; set;}
}
public class WorkoutExerciseViewModel
{
public int Id { get; set; }
public int WorkoutId { get; set; }
public int ExerciseId { get; set; }
public virtual ExerciseViewModel Exercise { get; set; }
public virtual IEnumerable<ExerciseSetViewModel> ExerciseSets { get; set; }
public string ExerciseFullname
{
get
{
return Exercise.Equipment.Name + " " + Exercise.Name;
}
}
}
public class ExerciseSetViewModel
{
public int Id { get; set; }
public int WorkoutExerciseId { get; set; }
public int Set { get; set; }
public int Reps { get; set; }
public int Weight { get; set; }
public string WeightValueType { get; set; }
}
There's really more to this than can reasonably be discussed in a StackOverflow answer, but I'll give you enough to start with.
As far as adding new exercises and sets within those exercises go, that's just JavaScript. You'll need to have some button that the user can click to add a new one, and tie the click event on that button to a handler that will add the appropriate HTML (form fields and such) to the page. There's many different ways to go about doing that, some more difficult than others. Most likely you want to look into some JavaScript templating library or a more full stack JS library like Knockout to make things easier. The only other thing to keep in mind is the conventions the modelbinder uses to wire everything from the post body to an instance of your model. For collections, it expects fields to have name attributes in the form of CollectionPropertyName[N].PropertyBeingEdited, where N is the position within the collection. So, the name attribute for ExerciseFullName for the first exercise would be WorkoutExercises[0].ExerciseFullName.
Your post action would simply take your same main view model:
[HttpPost]
public ActionResult Create(WorkoutViewModel model)
{
...
}
As long as you follow the property naming conventions for all the fields in your form, the modelbinder will happily wire everything from the post body onto your WorkoutViewModel instance.