I've searched all over the web but not finding the solution for the following problem:
Say I have three ViewModel classes
public class ViewModelNewPerson
{
public string PersonName;
public string Address;
public string EyeColor;
//etc
}
public class ViewModelSelectPerson
{
public int SelectedPersonId;
}
public class ViewModelComposite
{
public ViewModelSelectPerson selectViewModel;
public ViewModelNewPerson newPersonViewModel;
}
and I want to do the following things:
In the Controller I want to create a GET Action which uses the class ViewModelComposite as its Get model, and in the view I want the user choose from the following two available actions: to choose a existed person, and to add a new person as the selected value.
So I need to create two forms in the View, and there would be two POST Actions added to the Controller using the Post model of class ViewModelNewPerson and ViewModelSelectPerson.
My question is, how can I do the manual model binding using a Custom Model Binder that can convert the Composite class of ViewModelComposite to ViewModelNewPerson in the Action of create a new person, and to ViewModelSelectPerson in the Action of select an existing person?
EDIT:
Now I have an idea of decomposing the class ViewModelComposite and declare every property in the two classes into the composite class, and the default model binder will do the trick, I think. But that'll drop the composite pattern, and is not something I wanted.
You would use one single view model in your form, you would have a post action that receives your single view model.
In your Controller code:
public ActionResult GetSomeData(MyCustomViewModel model){
// add the first element
var person = Person.Add(model.Person);
// update the second object in model, with related / needed ID
model.PersonContent.PersonId = person.id;
// add in related content
var AddedContent = PersonContent.Add(model.PersonContent);
}
single form, multiple actions, multiple tables
Related
Anyone using DevExtreme to create ASP.NET MVC project here? I just want to create a simple drop down box that bind data source come from the controller. Here is how it works inside the controller, I create a list of object, then I add the model having two fields "Key" and "Value" into the list. So I pass the list of object in return view in the controller.
As you can see from the screenshot above while I debugging the View, the Model is the list of object that I passed in, it has data for sure. You can see there are two fields "Key" and "Value" from each item, and I also specified the ValueExpr and DisplayExpr. When I run the project, this is the result I get. I just don't know why it is still blank while my data does exist. Am I missing some code?
Without a little more detail it is tough to know for sure, but when I have run into this issue, I fixed it by checking:
If the model was in the wrong format (Not a list/IEnumerable)
The model class fields were set to private
I would also look at this link/demo DevExtreme has for SelectBox: https://js.devexpress.com/Demos/Widgetsgallery/Demo/SelectBox/Overview/Mvc/Light/
OR
DropdownBox:
https://js.devexpress.com/Demos/WidgetsGallery/Demo/DropDownBox/SingleSelection/Mvc/Light/
Otherwise, this is what I use to fill my chosen editor (I am using Dapper for SQL call):
View
#(Html.DevExtreme().SelectBox()
.ID("DemoSelectBox")
.DataSource(d => d.Mvc()
.LoadAction("GetDemoData"))
.DisplayExpr("DemoName")
.ValueExpr("DemoValue"))
Controller
[HttpGet]
public ActionResult GetDemoData(DataSourceLoadOptions loadOptions)
{
List<DemoData> demoDataGrid= new List<DemoData>();
using (SqlConnection connection = new SqlConnection(_connectionString))
{
demoDataGrid= connection.Query<DemoData>(
sql: #"SELECT DemoValue
,DemoName
FROM DemoDatabaseTable"
).ToList();
}
return Content(JsonConvert.SerializeObject(DataSourceLoader.Load(demoDataGrid, loadOptions)), "application/json");
}
Model Class
public class DemoData
{
public string DemoValue{ get; set; }
public string DemoName{ get; set; }
}
I'm fairly new to ninject so you'll have to forgive the potentially stupid question. I have been able to successfully bind my own custom filter to controller actions, however my question is can I do the same to a property on a ViewModel? My scenario is thus:
I have a view model with properties that look like this
public class CreateViewModel
{
...
[PopulateWith(typeof(Country))]
public IEnumerable<SelectListItem> Countries { get; set; }
...
}
the attribute is is a simple class
public class PopulateWithAttribute : Attribute
{
public Type Type { get; }
public PopulateWithAttribute(Type t)
{
Type = t;
}
}
and all I want to be able to do is have a block of code that will run when a new instance of CreateViewModel is created, that will take the database context to hydrate the enumeration. I'm not sure if even an action filter is the correct route to go down like how you make custom authorization / logging functionality. It doesn't seem that the BindFilter<> has anything that points to being able to bind on properties...
A point in any direction or to any resources would be great.
I am new to MVVM pattern and Caliburn.Micro. I've read some tutorials on how to get started, but I'm confused about the Model part of MVVM in the context of Caliburn.
I want to create my first MVVM application and I have some design questions:
In tutorials, the Model was presented as simple property in
ViewModel. How should I manage more complex models? Is there any
naming convention? Obviously, there should be some external classes
made for my models, but how should I communicate between my models
and the view?
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be
visible from different ViewModels?
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Thanks in advance for any help :)
EDIT:-------------------------------------------------------
Thank you for your anwser. I uderstand the topic more clearly, but I'm still confused about some details.
For an example, let's assume this little application. I have a form that allows me to add a new Customer. It has a few fields like Name, Surname etc.
After pressing the button, I invoke the addCustomer command in the ViewModel. I want my program to store the newly created customer inside the database.
My view also has the List control (whatever), which displays my customers as raw strings (like "Name: John, Surname: Doe, Address: ..." I know it's dumb to make it like this, but i need an example of model manipulation (like .toString()))
For this example, I've created a bunch of stuff to illustrate my vision of that process:
fields - it's a set of form fields like Name, Surname etc.
customerSet - it's a set of Customer class to store all created
customers
.addToDatabase(fields) - a method which puts newly created customer
to the database
.getStrings - a method which prepares a set of strings to be
displayed by the list in CustomerView
I think about 2 approaches that would be good for a solution:
First approach. I don't like this one. The only advantage is, that
ViewModel handles all the logic inside application. Sharing model
would be a serious problem here, because saving methods are bound to
the ViewModel class.
Second, MVC like approach. To me it's the most intuitive one. But - I
don't know where should I store CustomersModel object, so few
ViewModels could have access to it.
Which is the better one? Or maybe another approach that is more suitable for MVVM?
Another problem is: Where should I put my method that will load all the Customers from the database, so they could be displayes on the list? In "get method" inside viewmodel, or inside a model class?
In tutorials, the Model was presented as simple property in ViewModel.
How should I manage more complex models? Is there any naming
convention? Obviously, there should be some external classes made for
my models, but how should I communicate between my models and the
view?
Your models should represent whatever it is they need to whether it's a customer, account, etc. The view models job is to handle the interaction between the view and models.
How should I keep references to many instances of one complex model?
For ex. cumtomers (instances of Customer model class)
Generally, you will map complex models to more friendly format for display, you can do it manually or use a tool like AutoMapper.
Is there a possibility to manipulate one model class in many
ViewModels? How should I store my model reference, so it'll be visible
from different ViewModels?
If you're working with a local db you can pass IDs around. If it's a service you could persist the model locally for other view models to work with. You could also inject a singleton, ISharedData, into view models that need to work with shared data.
Where should I put my code for more complex model manupulation/file,
database storage? How should I invoke such code? I'm not asking here
about SQLConnections, but MVVM best practices. :)
Create services for more complex model manipulation / business logic. Inject the services into view models that require them. ICustomerService, IAccountService, etc.
EDIT:-------------------------------------------------------
You're first approach is correct. To your point about sharing the model being a serious problem because saving methods are bound to the view model class. The view model will have a SaveCustomerCommand that is fired when the button is clicked, because of its binding.
The SaveCustomerCommand will persist the CustomerModel, regardless of how the CustomerModel is persisted. So if its a database, the view model might have a reference to a context and issue a _db.Save(CustomerModel). If another view model needs to manipulate a CustomerModel, it will do so by using the context. The view model could also have a reference to a CustomerService that handles the crud for the CustomerModel.
Here's how this might look:
public class AddCustomerViewModel : Screen
{
private readonly ICustomerService _customerService;
public AddCustomerViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
//If button is named x:Name="SaveCustomer" CM will
//bind it by convention to this method
public void SaveCustomer(Customer customer)
{
_customerService.Save(customer);
}
}
public class CustomerListViewModel : Screen
{
private readonly ICustomerService _customerService;
private List<CustomerDisplayModel> _customers;
public CustomerListViewModel(ICustomerService customerService)
{
_customerService = customerService;
}
public List<CustomerDisplayModel> Customers
{
get { return _customers; }
set
{
_customers = value;
NotifyOfPropertyChange();
}
}
//only fires once, unlike OnActivate()
protected override void OnInitialize()
{
var customers = _customerService.LoadAllCustomers();
//could just use the model but this shows how one might map from
//the domain model to a display model, AutoMapper could be used for this
Customers = customers.Select(c => new CustomerDisplayModel(c)).ToList();
}
}
public interface ICustomerService
{
List<Customer> LoadAllCustomers();
void Save(Customer customer);
}
//same as button, Label named x:Name="CustomerName" will bind
// to CustomerName
public class CustomerDisplayModel
{
private readonly Customer _customer;
public CustomerDisplayModel(Customer customer)
{
_customer = customer;
}
public string CustomerName
{
get { return _customer.Name; }
set { _customer.Name = value; }
}
public string Surname
{
get { return _customer.Surname; }
set { _customer.Surname = value; }
}
public string Address
{
get { return _customer.Address; }
set { _customer.Address = value; }
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Address { get; set; }
}
This is for an asp.net mvc3 project. I have two views and their own corresponding viewmodels.
Home.aspx has a viewmodel HomeVM
HomeChild.aspx has a viewmodel as HomeChildVM.
Now HomeChildVM is derived from HomeVM and has a few properties more that are used in its own view. My controller has one action method that returns the Home view and another action method that returns the HomeChild view. Both these action methods call one business method that returns a type as HomeVM. Then my action methods return the same to the aspx view.
return View(objHomeVM);
Now, instead of writing another business method and repeating all the code all over again just to return another viewmodel type ie HomeChildVM, I am assigning the properties of objHomeVM to objHomeChildVM one by one like so:
objHomeChildVM.prop1 = objHomeVM.prop1;
objHomeChildVM.prop2 = objHomeVM.prop2;
and then returning it:
return View(objHomeChildVM);
Is there a better way to do this than by assigning properties one by one? I feel this is too primitive a way, unless this is the only way to do it.
HomeChild.aspx currently has this page directive
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<HomeChildVM>" %>
If I change that to HomeVM then I wont be able to use the extra properties that HomeChildVM has which are only specific to HomeChild.aspx page.
Any thoughts on this?
Thanks for your time...
Have you considered creating the required ViewModel outside the business method and then passing a reference of the ViewModel into your business method, as a type of HomeVM, to be populated? ie:
public ActionResult HomeAction()
{
HomeVM objHomeVM = new HomeVM();
BusinessMethod(objHomeVM);
return View(objHomeVM);
}
public ActionResult HomeChildAction()
{
HomeChildVM objHomeChildVM = new HomeChildVM();
BusinessMethod(objHomeChildVM);
return View(objHomeChildVM);
}
private void BusinessMethod(HomeVM objHomeVM)
{
...
objHomeVM.prop1 = prop1;
objHomeVM.prop2 = prop1;
...
}
Remember ViewModels are simply normal objects. The only thing special about them is that they are called ViewModels.
Just a note on your view models. I would create a view model for both views, even though some of the properties are used in both. What are you going to do if you have to remove one of the properties in HomeVM? Then HomeChildVM falls apart.
Getting back to your question on assigning the properties one by one.. I would suggest you look at Auto Mapper. It takes care of mapping the properties between objects for you.
UPDATED
Once you have your types, and a reference to AutoMapper, you can create a map for the two types:
Mapper.CreateMap<Customer, CustomerDto>(); // Create the map
The type on the left is the source type, and the type on the right is the destination type. To perform a mapping, use the Map method:
CustomerDto dto = Mapper.Map<Customer, CustomerDto>(customer);
Here is some sample code that you could write/use to map between 2 objects:
public static Customer Map(CustomerEntity entity)
{
return new Customer
{
CustomerId = entity.CustomerId,
Company = entity.CompanyName,
City = entity.City,
Country = entity.Country
};
}
And then to use it would look something like this:
Mapper.Map(customer);
I have page with a simple table and advanced search form. I pass List<Customers> to the model:
View(List<Customers>);
So what is best way to pass and return data to the search form? I want to use validation or something but I think passing data through ViewData is not good idea. Any suggestions?
You should wrap all your data that is required by you view in a model specific to that view. The advantage to this is you could also include your search criteria in the model which would be empty at first but when your search posted, the model would automatically contain your search criteria so you could reload it when passing back the results. This will help maintain your state between post's as well.
This also allows all your view's data to be type safe where ViewData would not be.
Eg:
public class CustomerSearchViewModel
{
public List<Customer> Customers { get; set; }
// your search criteria if you want to include it
public string SearchFirstName { get; set; }
public string SearchLastName { get; set; }
public int SearchCustomerID { get; set; }
// etc...
}
When you return back the List<Customer> the search criteria would already be filled in your model from the post so your view can default the search criteria back to the corresponding controls (assuming your search results and search inputs controls are on the same view).
For example, in your post you would accept a CustomerSearchViewModel. Then all you need to do is get your list of customers and add it back to the model and return the same model.
// assuming you have accepted a CustomerSearchViewModel named model
model.Customers = GetCustomersForSearchCriteria(model.SearchFirstName,
model.SearchLastName, model.SearchCustomerID);
return View(model);
You could also add the validation attributes to your model properties to leverage the built in validation in MVC. This would not be possible if you were using ViewData to pass this data around.
You have to also consider the 'next guy'. It's cleaner when all the data that the view requires is located in a single class. This way they don't have to hunt through the code to discover if ViewData is being used and what data is actually being passed around in it.
ViewData is still an option for passing data but I try to minimize the use of it if at all possible.
Rather than passing just a list of items to your View, create a class which contains your list of items and any other data you might need, i.e. a ViewModel.
public class CustomerSearchViewModel {
public IEnumerable<Customer> Customers { get; set; }
public string SearchTerm { get; set; }
}
.....
var viewModel = new CustomerSearchViewModel {
Customers = customerList,
SearchTerm = searchTerm
};
return View(viewModel);