I'm tying myself in knots - and thought it best to take a big step back, and back to basics.
I understand I should be using a ViewModel, so that is what I'm trying to contruct.
My demo app will have 4 sections (4 different parts of a form to complete):
Get Date/Number of days from user
Use that data to query the database, and return a list of qualifying records - each of these will have a unique ID of TypeID - and for each of these, they should also have 2 dynamic DropDownLists associated with them (so that whatever is selected in ListBox3 for each of the lists, corresponds to the TypeID3 (and whatever ID that has)
user will then be able to select Extras, again from a drop down list populated dynamically
users Name/Add/Tel will be collected
My "View" of what the ViewModel needs to look like/hold is:
I beleive my viewModel should look something like this:
public class SearchViewModel
{
public DateTime Date{ get; set; }
public int Days { get; set; }
public IQueryable<TypeID> TypeIDs { get; set; }
public IQueryable<LB1Item> LB1Items { get; set; }
public IQueryable<LB2Item> LB2Items { get; set; }
public IQueryable<Extras> Extras { get; set; }
public string Name { get; set; }
public string Add { get; set; }
public string Tel { get; set; }
public string email { get; set; }
}
First of all - is this how you would construct a ViewModel for what I've described above? I'm not certain of the DropDown Boxes, as for each form, there could be 1, 2, 3....10, 11, 12 for each TypeID retrieved - based on the Date selected.
Each of the drop down boxes for LB1Item and LB2Item - need to have their selected values stored against the TypeID for each line also.
This is what I think the class should look like for 1 drop down:
public class LB1Item
{
public String TypeName { get; set; }
public long TypeID { get; set; }
public int NumSelected { get; set; }
public int TypeCount { get; set; }
public IEnumerable<SelectListItem> CarsAvail
{
get
{
return new SelectList(
Enumerable.Range(0, TypeCount+1)
.OrderBy(typecount => typecount)
.Select(typecount => new SelectListItem
{
Value = typecount.ToString(),
Text = typecount.ToString()
}
), "Value", "Text");
}
}
}
Does that look ok also? Or am I overcomplicating what I'm trying to achieve?
I'd also like, after POSTing back the data after each stage (1, 2, 3, 4) to be actively populating the ViewModel with the selected values - and passing it back down to the view, so that I can retrieve it for the next Step.
What I want to end up with is something like this:
Date: 01/09/2012
Days: 4
{ List:
TypeID: 3059 ListBox1: 2 ListBox2: 8748,
TypeID: 2167 ListBox1: 7 ListBox2: 2378,
TypeID: 4983 ListBox1: 4 ListBox2: 5873
}
{List:
ExtraID: 4324,
ExtraID: 3878,
ExtraID: 4872,
ExtraID: 7698,
ExtraID: 2873
}
Name: Mark
Add: My town
Tel: 0912378
Email: me#me.com
Thanks for any help/pointers/samples...
Mark
For this type of solution I would separate out each section you want to render as individual views and use Ajax calls using Jquery ajax method. I would also use KnockoutJS to handle the views in your client. So you will essentially have two ViewModels, one in JavaScript in the client and one in MVC for returning the pieces you need as JSON for the Ajax calls from the client. Section 1 of your view is entered by the user into the client so you do not need it on the Controller side. Section 1 is basically the data used to query for Section 2. No need to make your collections IQueryable either since you will not being querying the lists that are returned. Your ViewModel on the Controller side might look something like this:
public class Section2
{
public List<TypeID> TypeIDs { get; set; }
public List<LB1Item> LB1Items { get; set; }
public List<LB2Item> LB2Items { get; set; }
}
public class Section3
{
public List<Extras> Extras { get; set; }
}
public class Section4
{
public string Name { get; set; }
public string Add { get; set; }
public string Tel { get; set; }
public string email { get; set; }
}
So the steps that would be taken are that when an event is thrown that the date and days have been entered by the user an Ajax call is made back to a controller with entered data and days to query for the information that will populate Section 2. The controller returns the Section2 ViewModel as JSON and it is rendered in the HTML using Knockout. Then when the user selects from the lists in Section 2 an event is thrown to query the controller again to return the information needed to populate Section 3, and the cycle is repeated.
There is an excellent example on using Knockout to do exactly what you are trying to do here.
Related
Okay, so I'm in the middle of an ASP.NET project in work at the minute and it's all going well. So far I have all the models, views and controllers made and they all work perfectly, but now I have been told that I need to make a class (lets just call it results to make life easy here) to display all the data. This is an example I was given for what the displayed results would look like:
There are multiple different displays at the moment for different things like a person, travel insurance, car insurance etc. and the only common things between each different display are the full name, contact email, contact number, contact address and recorded time stamp. It's the additional data that changes for each display depending on what is being requested, i.e., person displays all the information stored about the person like full name, age, DOB, mobile number, home address and so on while travel insurance displays all information stored about the travel insurance for the person who is asking like the trip type, trip destination, group type (single/couple/family/friends), travelers ages and so on..
My question is this: how do I create this results class to display the required data? Do I need another controller, model and view or do I just need to create a class called Result and put everything into that? (I think I just have to create a class but I am not sure, hence why I am asking) Also, how do I make it in such a way that different things are displayed in the additional data depending on what the person searched for? Like I don't need all the additional data about car insurance appearing when a person searched their travel insurance - does this mean I will need multiple forms of the results class which all refer to a different searchable thing or can everything go in the one class?
Apologies if this is awfully worded and/or a bad question - I did try looking for something to help myself before posting here but I couldn't find anything. I'm also quite new to the whole ASP.NET front so apologies if I'm missing anything obvious.
Create a base Model class and put all the common properties to be displayed in that, and derive other Model classes from that class with their specific properties in them. You can pass Person object to the view where Person's info need to be displayed and TravelInsurance object where Travel Insurance's info needs to be displayed.
public class ModelBase
{
public string FullName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public DateTime TimeStamp { get; set; }
}
public class Person : ModelBase
{
public string DateOfBirth { get; set; }
public string MobileNumber { get; set; }
}
public class TravelInsurance : ModelBase
{
public string TripType { get; set; }
public string TripDestination { get; set; }
}
If that does not sound like the way to go, you can have a generic Model class with a generic Data property within it:
public class Model<T>
{
public string FullName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public DateTime TimeStamp { get; set; }
public T Data { get; set; }
}
public class Person
{
public string DateOfBirth { get; set; }
public string MobileNumber { get; set; }
}
public class TravelInsurance
{
public string TripType { get; set; }
public string TripDestination { get; set; }
}
where you can pass objects to your view like this:
var x = new Model<Person> { FullName = "...", Data = new Person { } };
OR
var x = new Model<TravelInsurance> { FullName = "...", Data = new TravelInsurance { } };
I have the following view models
public class Step4ViewModel {
[Required(ErrorMessage="Yes/No Required")]
public bool? HaveVehicles { get; set; }
public List<Vehicle> Vehicles { get; set; }
public Step4ViewModel() {
this.Vehicles = new List<Vehicle>();
}
}
public class Vehicle {
public string Index { get; set; }
[DisplayName("Registration/Vin")]
[Required(ErrorMessage="Registration Required")]
[ValidVehicleRegistrationNumber(ErrorMessage = "Invalid Id Number")]
public string Registration { get; set; }
[Required(ErrorMessage="Make Required")]
public string Make { get; set; }
[Required(ErrorMessage="Model Required")]
public string Model { get; set; }
[Required(ErrorMessage="Year")]
public string Year { get; set; }
public Vehicle()
{
this.Index = Guid.NewGuid().ToString();
}
}
The validation works just fine. The problem is that it always works. I only want the validation to fire on vehicles in the collection when the user has indicated that they have vehicles. If HaveVehicles equals false I do not want the validation to fire.
Normally for this sort of validation I would build a custom validator in which I would only return validation errors if HaveVehicles is true. I am unable to do this here because HaveVehicles resides in a parent model which is inaccesible from the within the vehicle object.
One approach I've though of that may work is using a partial view to add/remove the collection from the DOM as null collections aren't validated. If the user selects yes ajax loads a partial view which contains a for loop that iterates over each vehicle and calls another partial view to display it.
Is there a better approach to do this or is something similar to the above my only option?
I'm quite stuck with ASP MVC Model's, Now I understand how to create a simple model so I could set some values like this:
namespace build_01.Models
{
public class NewBooking
{
public int bookingID { get; set; }
public string bookingName { get; set; }
}
}
Now what I'm trying to do is have a Model that has all the bookingNames for example such as:
namespace build_01.Models
{
public class BookingNames
{
public string administrator { get; set; }
public string normal { get; set; }
public string user { get; set; }
}
}
However, what I would like to do is get this data from a database, for example I could have adminsistrator, normal, user, superuser, banned or whatever I like but the idea is that this information in the database can be changed. The bookingNames can be added, edited or deleted.
So let's say I just added superuser and banned to the database my model would now look like this for when I send it to the view and submit a form from the view to send to the HTTPPOST controller:
namespace build_01.Models
{
public class BookingNames
{
public string administrator { get; set; }
public string normal { get; set; }
public string user { get; set; }
public string superuser { get; set; }
public string banned { get; set; }
}
}
In my view I'm basically making checkboxes based on this information however I need to be able to set and get the values in a model so I can access them in my HTTPOST controller.
Now i've had a look at the Entity Framework but that doesn't seem to be what I want.
Is it possible to be able to create a model with the actual rows or even a column from a row in order to send it to the view to create a form for those columns so that I can return the values to the HTTPPOST in order to input data into a row in a table?
If so how can I approach this?
asp.net mvc 4, Entity Framework 5, SQL Server 2012 Express
I have a Place model:
public virtual int PlaceID { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual string Name { get; set; }
and a related Tag model:
public virtual int TagID { get; set; }
public virtual string Name { get; set; }
public virtual string NamePlural { get; set; }
public virtual ICollection<Place> Places { get; set; }
many to many relationship.
There is a view with the following ViewModel associated. In it I can edit place details - and edit what tags are associated with the place (for example, the place might have a 'restaurant' tag and a 'bar' tag - and perhaps I want to add a 'cafe' tag, and remove 'restaurant' tag).
PlacesWithTagsViewModel:
public Place place { get; set; }
public ICollection<Tag> SelectedTags { get; set; }
When the view does an httpost back to controller - I update tags like this:
place.Tags = SelectedTags
db.Entry(ptvm.place).State = EntityState.Modified;
db.SaveChanges();
However, place properties update (eg Name) - but Tags always stay the same.
How can I update tags?
Avoid db.Entry(ptvm.place).State = EntityState.Modified; as it causes conflicts with no updation.
You must use UpdateModel(table object, "model");
Full Example is as belows :
[HttpPost]
public ActionResult PlaceTag(PlacesWithTagsViewModel model)
{
if (ModelState.IsValid)
{
tag tagtest = GetTagById(model.tagid);
tag.name= model.tag.name;
tag.nameplural = model.tag.nameplural;
UpdateModel(tag, "model");
db.SaveChanges();
return RedirectToAction("Index", "Dashboard", new { id = 5 });
}
}
The advantage of UpdateModel is that you have to mention only those fields which you update avoiding those which remain static. In this way you can update your related data with Viewmodel in Edit View.
I got 2 Models with 1-1 relationship.
public class CustomerModel
{
public int Id { get; set; }
public string Name { get; set; }
public AddressModel Address { get; set; }
}
public class AddressModel
{
public int Id { get; set; }
public string Street { get; set; }
}
Now I need a view that I can link the Address model with the customer, so, in the create of the customerModel, it bring the address create too, and linked, like in the post the address will be in the customer field.
#model Mvc.Models.CustomerModel
#Html.EditorForModel(Model)
#Html.EditorFor(x => x.Address)
If you create your view like that, you will then able to post back to an action result that takes a CustomerModel and the binding should work correctly
You'll probably want to do a bit more with custom annotations etc. as I doubt you'll want the user to be able to edit the Address id, but that should point you in the right direction