Not sure if the title is clear enough. I have Products and Categories entities in an MVC-EF app. when adding a new product, I select the category from a drop down list. I would like to be able to create a new category if it doesn't exist in the drop down list from within the product/create page. maybe having a button like "Add New Category" next to the drop down list.
For example: if I want to add a book that belongs to the Cooking department (which is not in the list), I want to be able to add the cooking department at the same time I'm adding the book without going to the Department/Create view
Well, there's three primary approaches:
Handle the category creation through a modal window or similar that pulls in an iframe. This is the easiest method as the category creation is completely segrated with its own actions and views. However, you'll still need some JavaScript to update your select list choices to include the newly created item after its saved. That's relatively easy, though. You just issue an AJAX request to some endpoint that returns all the available categories and use that to rebuild the select list after the modal is closed.
Use AJAX for category creation. This is a little more complicated than #1, but still relatively straight-forward. You simply have a form with the fields for a new category embedded somewhere in your page. You can also use AJAX to request the form HTML from an action that returns a PartialViewResult. Either way, you can present this to the user through a modal or whatever you like. Then, when they submit, instead of a traditional form post, you simply issue an AJAX request with the serialized form data to some endpoint responsible for actually saving it. Like #1, you'll need additional JavaScript to update your select list accordingly after saving the category.
Include the new category with the post data of the rest of the product form. This is the most complicated of the three choices as you have to take special care in a few ways. You'll need a separate collection on your view model to hold the posted category or categories, and you'll need to manually add these new categories to your product entity before saving it. You'll also need to synchronize it with categories associated via the select list.
Giving you anything more specific is beyond the scope of StackOverflow. Whichever of the above three methods you choose, a little bit of research will turn up enough articles/tutorials to get you rolling. If you have specific issues in the process of building your solution, you can come back here to ask specific questions about those.
I have had a similar requirement on a project. Make something like this:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
Then you populate the dropdown with all your categories, and you can make more as you go along.
On your product you just make a virtual property of category to access it.
public class Product
{
public int Id { get; set; }
//Rest of your fields
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
Doing this will allow you to lazy load the related entities if you wish.
Related
My title is probably terrible because I'm having trouble wording what I am trying to do.
I have an object that can potentially contain a huge number of records that looks something like this:
public class AssignmentGenerator : BaseGenerator
{
public bool IsLibrary { get; set; } = false;
public List<LineItem> LineItems { get; set; } = new List<LineItem>();
}
public class LineItem
{
public string Name { get; set; }
public string Value { get; set; }
}
I have a form created that allows editing of the values of the object, but it is possible for the list of line items to become very large (one example I have is ~ 3000). This being the case, I would like to make the line item list be a paged list in my view allowing editing of say 10 to 50 items at a time.
I've read a lot of tutorials and posts about how to do paging but none of the ones I've found go into how to do editing of a large set of data. I don't want to save the changes on each page to the database until the user actually clicks on the Save button. Is there a way to store the values in the object, retrieve them as needed, and then save upon user action?
The short answer is yes, there's a way - you're the programmer, you can do what you want. It's hard to give real code examples without more details, so below is just vague guidance.
You have to store their changes somewhere, but you can choose to save them in a staging database, or keep your AssignmentGenerator in memory on the server and just update the collection when they page (assuming 1 server or pinned sessions).
You will have to post the current state of the objects as the user changes pages (instead of just a Get endpoint). You don't have to save to the real database; you just update your temporary copy. The Save button should trigger a different controller action which moves your temporary copy to the real data store.
I'm getting mixed answers reading through other posts, but say I have one main ViewModel, which handles two related data models
public partial class Product
{
public string ProductName { get; set; }
public int TemplateID { get; set; }
public virtual Template Template { get; set; }
}
public Template()
{
this.Products = new ObservableCollection<Product>();
}
public int TemplateID { get; set; }
public string TemplateName { get; set; }
public virtual ObservableCollection<Product> Products { get; set; }
}
Currently, have two separate Views and ViewModels, one that shows all data "byTemplates" and another that shows all the data "byProducts". Both allow CRUD operations. In order to get rid of duplicate codes and commands, I want to merge these two views into one.
Would I still require two ViewModels? Can I have both working off the same data instance? (so if I went and inserted something under the "ByTemplates" view, and switch to "ByProducts" before saving to the database, I'd still see all the changes I have?
EDIT:
I'm currently using tabs to faciliate this.
My views are here:
By Product Tab
By Templates Tab (user can select a Template, and the "Associated Products" ListView will show all prodcucts linked to "template")
What you are saying is pretty much possible, viewmodel can contain multiple models combined together acting as one parent viewmodel and it is up to view which all properties that it is interested in binding with.
Having two separate views or not is a design discussion but it is not mandatory. Neither having one viewmodel per model is required. Purpose of having view model is to get rid of direct dependency on a particular business model and merge and mold data according to UI needs. So yes you can do what you intend to do.
Please share detail xaml and existing model/viewmodel if you want in depth how to do, else you are all set.
I would tend to use a single viewmodel where both views are interconnected. An example being detail and summary view of the same data perhaps.
In the scenario you describe, if both views are leveraging the same data and methods then sure, keep them as one viewmodel. If however you are parametising each command to work on a subset then it sounds as though seperation will be better - I would look to do this by base classing the common elements to improve reuse and maintenance and then extend into specific viewmodels for each subset. That also offers better extensions later.
So I'm not even certain that I'm approaching this the correct way, so if not, please, by all means suggest another approach.
The situation is that I would like to have a main View, with 1 of multiple child views that will change dynamically. For simplification, let's say my main object/entity is "person", which has typical person fields like first name, last name, etc. it also has optional relationships to other tables which contain profession specific information. So on my pages to create, edit, etc. I'd like to offer a element with the list of professions, that will dynamically replace some <div id="professionData"> with a strongly typed partial that I've created. This create form would have a single submit which submits the parent and child models.
I've actually got a semi-working solution in place, which on the MVC controller creates a new object based on the type of profession passed in, and hands that off to the partial view as the model.
The problem that I'm having currently is that I'm not sure how to link that child model back to the model used for the main page.
My thought was to just link it on the MVC controller, something like
person.doctorData = emptyDoctorData;
Where person is an instance of the person object(reloaded from EF, from passed in ID)
I've learned how to create my child object dynamically with a passed in string that identifies the type, ie "doctorData"
But I'm not sure how to access the navigation property on person when I only have the string. it'd be nice to be able to do something like.
public PartialView LoadPartial(int personId,string extraDataType){
PersonType person = repository.FirstOrDefault(x.Id == personId);
var extraData = Activator.CreateInstance(extraDataType);
person.Property(extraDataType)=extraData;
string partialURL="Views/Partials/"+extraDataType;
return PartialView(partialURL,person);
}
The CreatInstance is a very sortened version,and won't work in this scenario as is. This is not my actual code, but overly simplified.
The person.Property line is the one hanging me up. Not sure how to set the navigation property only having the string that identifies it. Plus I'm not even sure this is the "correct" way to go about it. Once I get back to the view, will MVC properly link, this new person model in the partial, with the one that is in the parent view? will they be properly linked on the submit/post?
Also right now, the partial contains it's own form, but I think I need to take that sub-form out and just include it in the parent view (ie. "Person") form.
The person class, which is generated. We're using the EF DB modeler. would look something like.
public class Person{
public Person();
public int Id {get;set;}
public string LastName {get;set;}
public string FirstName {get;set;}
public string Profession {get;set;}
public virtual DoctorData DoctorData {get;set;}
public virtual TeacherData TeacherData {get;set;}
public virtual LawyerData LawyerData {get;set;}
public virtual ClerkData ClerkData {get;set;}
}
I have a complex form, with hierarchy of view models and I am wondering how to structure the code so that my controller will not contain handlers for all actions.
Here is a simplified example:
with the corresponding ViewModel:
public class MyPageViewModel
{
public List<TabViewModel> Tabs {get; set; }
public CustomerViewModel Customer;
}
public class TabViewModel
{
public string DisplayLabel { get; set; }
public bool Selected { get; set; }
}
class CustomerViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}
I do know how to separate rendering of each part of the page in separate components: I am using #Html.EditorFor and #Html.DisplayFor and have a separate view for each part of the model (indicated by the red rectangles on the picture above). This works quite nice. Parts of the ViewModel (for example TabViewModel class) can also be re-used on other pages.
I am having problems with event handling logic. There are quite few actions that can be performed on this page (indicated with blue background). One possibility would be to use multiple FORM tags – each for one red rectangle on picture above. Each form would have a different action URL and would be handled by a different controller. But when using this approach, I could lose same data. For example: if the user changes First Name and then hits the Remove Address button the First Name would not be POSTed back to the server and the change would be lost.
This leaves me with one form for the whole page. That means that all actions should be processed by a single controller class. I really do not like this approach, because:
I’ll end up with big, fat controller containing action handling code for all buttons
Or I’ll have a big switch statement within my controller, which would identify the action, locate separate class, that know how to handle the action and then delegate the processing to it (yuck! This sound like writing windows messages event handling code in 1990s – WindowProc)
I’ve read about Html.ActionFor which enables you to call a separate controller from a View, but I do not think this is good approach either (it happens while view is being rendered, which is not OK).
So, to sum up, here’s the question again: Is there a way to handle the actions/events triggered from different parts of complex view model in such a way that I will not end up with a mess in my controller? What are the best practices for a real-life applications (not 101 CRUD examples that are generated by Visual studio scaffolding )
UPDATE: Please note that this is just a simplified example - in the reality, the view model is much more complex and there are many more actions, that can be performed. I am asking about a general approach for structuring the code in controller (or moving it to separate classes) in ASP.NET MVC application. The WebForms provided user controls, which enabled us to encapsulate both the View part (ASCX) and the event handlers. MVC has a good solution for encapsulating the views and I am trying to find the right way to structure the logic/event handlers.
I would add a "Save" button for the customer data fieldset and save/post all of the customer data when the user hits the Save button. This way on your Edit Post Action you will only use CustomerViewModel.
"Add new address" and "Remove this address" will add and remove the html needed for Address with javascript.
"Search for existing customer" and "Select from external DB" can show a pop up with data needed for their respectful sections in the form. Use Ajax to get the data.
This way you will have a simple Edit Post Action for the CustomerData fieldset, 2 Actions for displaying the data in in pop ups, and some javascript for manipulating the addresses section.
I'm currently experimenting with the Dynamic Data Entity Web App Project type in VS2008 SP1 and after reading many tutorials which offer helpful advice for problems I so far have no need of a solution to I have fallen at the first hurdle.
In the DB I have made my entity model from I decided to start small with a table called "Companies" just to see if I could tweak the display into a satisfactory shape for this small table. The Companies table has a column called "contactid" which leads to a record filled with various contact information in a "contacts" table.
The default created Entity Data Model has guessed that One companies could have many contact records. So it tries to be helpful and add a "Contact" filter onto the page that allows you to see all the Companies that share a particular set of contact info indexed by the "Contact Name" field.
Unfortunately the contact table is a multi-purpose one that also stores contact info for customers and there are about 1000 times more customers than there are companies. So the Dropdown makes the page load time increase exponentially and produces no benefit.
So I'd like to just stop the filter from appearing. Only problem is I don't have a clue how to switch it off. Google is so far proving recalcitrant on the matter so I wondered if anyone in here knew how to get rid of a useless filter.
I recently had the same issue. I am new to the Dynamic Data Application template and was quickly impressed by its out of the box functionality. However I noted a few set backs as well, specifically the one you mentioned. Several of my pages took forever to load, and when I disovered that it was caused by the foreign key drop down populates, it took me a while to find a solution.
The problem is this behavoir is essential to the inner workings of the web site. These foreign key dropdown lists actually drive the result set in the datagrid. (The ddlist is populated, then the default value is set, which raises an event to filter the datagrid.) So the key is to alter this behavoir instead of disabling it.
My first clue in solve this issue came from an excellent series of blogs I found on the web. Kudos to Stephen Naughton
The concept is to create a metacolumn attribute and decorate the problematic entities in your metatable partial classes. (I named mine DisableFilter)
So, you'll end up with a table and metatable partial class definition similar to:
[MetadataType(typeof(InvoiceMetadata))]
public partial class Invoice {
}
public partial class InvoiceMetadata {
[ScaffoldColumn(false)]
public string InvoiceId { get; set; }
[ScaffoldColumn(false)]
public string LiquidatorInvoiceId { get; set; }
[ScaffoldColumn(false)]
public string CreatedBy { get; set; }
[ScaffoldColumn(false)]
public System.Nullable<System.DateTime> LastModifiedDate { get; set; }
[ScaffoldColumn(false)]
public string LastModifiedBy { get; set; }
[DisableFilter]
public LeadAccount LeadAccount { get; set; }
}
My newly created attribute, DisableFilter, decorates the LeadAccount (Our Contact table) collection.
Once the column is decorated, I then altered the base behavior of the ForeignKey.ascx control by adding a check for the new attribute. If the filter is disabled, I only populate the ddlist with the default item.
if (!Page.IsPostBack) {
if (!Column.IsRequired) {
DropDownList1.Items.Add(new ListItem("[Not Set]", NullValueString));
}
if (Column.IsFilterDisabled()) {
DropDownList1.Items.Add(new ListItem(DefaultValue, DefaultValue));
}
else {
PopulateListControl(DropDownList1);
}
// Set the initial value if there is one
string initialValue = DefaultValue;
if (!String.IsNullOrEmpty(initialValue)) {
DropDownList1.SelectedValue = initialValue;
}
}
}
Its not quite what I call a full solution, mainly the value of the filter will not contain a user friendly name. I haven't bothered to resolve this yet, but this should get you one step closer to solving your problem.