Change ViewModel default data strictly from server in MVC? - sql

I know this is a weird one but bear with me. I need to set a default state/value for form inputs on a portion of my application.
In my "MainController" I pull up a portion (or 'wrapper' of sorts) page and then pull partial views from this main page. Here's a pseudo-code example:
User goes to Main ->
MainController/Index Called
-> User clicks Link A ->
AJAX .load() pulls html from PartialViewA into #partialContainer
-> User clicks Link B ->
AJAX .load pulls html from PartialViewB into #partialContainer
Here's the AJAX call:
$("#mainPanel").load('#Url.Action("GetModule","Settings")' + '?partialName=' + moduleName);
...and the corresponding server-side action that handles it:
public ActionResult GetModule(string partialName)
{
return PartialView(partialName);
}
It works great for me, each of the modules has plenty of form fields on them, all interacting well with one another and server so that isn't my problem. The issue is setting default values from the dbase for the form fields contained in the partial views.
For instance the "General" partial has many checkboxes which will determine how portions of the application display. I want to pull from the database the pre-exisiting boolean value and when the partial gets pulled from GetModule(), have these values defaulted.
I've taken a look around and I'm afraid the way that I am pulling the partial's into the main page may be the issue. I thought I could build the defaults into the constructor like so:
public class GeneralViewModel
{
public GeneralViewModel()
{
var Data = from m in dataContext.Table
where m.UserID == _id
select new
{
m.Data1,
m.Data2,
};
foreach(var setting in Data)
{
Checkbox1 = Convert.ToBoolean(setting.Data1); // Conversion from bool? to bool
Checkbox2 = Convert.ToBoolean(setting.Data2); // Conversion from bool? to bool
}
}
public bool Checkbox1 { get; set; }
public bool Checkbox2 { get; set; }
}
But it would appear the constructor never gets called. That sort of makes sense, except when you consider the fact that my form fields are not only rendering properly, but communicating with the database just fine as well. So the question is, what am I doing wrong? Is it the way I call the Partial's or am I missing something with assigning values to my VM values?
As always, thanks SO!

I think it's better to have different action methods for rendering the partial views, but for your case, i think this solution would work.
Have a Model that contains the other view models
public class ViewModel
{
public ViewModel1 ViewModel1 { get;set;}
public GenereViewModel General {get;set;}
}
Then in your controller you could initialize the viewmodel based on the partial name.
public ActionResult GetModule(string partialName)
{
var model = new ViewModel();
switch (partialName)
{
case "General": model.General = InitializeGeneral();
break;
case "ViewModel1": model.ViewModel1 = InitializeViewModel1(); break;
}
return PartialView(partialName, model);
}
private GeneralViewModel InitializeGeneral()
{
// initalize then return model
}

Related

How to bind dynamic complex objects created using partial-view to a collection property in view-model

I'm unable to bind a collection of child-complext objects created dynamically using a partial-view to view-model IEnumerable property.
I have successfully bound objects created dynamically using partial-views to a view-model using a technique I found on this blog https://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/. I have followed the same technique but I'm unable to bind a collection to a IEnumerable property in a view-model.
[BindRequired]
public class EmployeeViewModel
{
other properties....
public IEnumerable<ContactDetailViewModel> EmployeeContact { get; set; }
}
[BindRequired]
public class ContactDetailViewModel
{
// I use this as my indexer for dynamic elements
public string RecordId { get; set; } = Guid.NewGuid().ToString();
public string Telephone { get; set; }
public string EmailAddress { get; set; }
public string ContactDescription { get; set; }
}
I call into this action-method via ajax to add dynamic contact detail elements and it returns the partial-view as html and it works fine.
[Route("[action]", Name = "BlankEmployeeContactDetail"), HttpGet("AddBlankContactDetail")]
public PartialViewResult AddBlankContactDetail()
{
return PartialView("_ContactInformation", new ContactDetailViewModel());
}
The initial contact detail is added to the main-view using the following, kindly follow this link https://1drv.ms/u/s!AkRSHVUtFlKhuHaxH96Ik4ineATE to download the main view and partial-view cshtml files. It is also noteworthy to mention that model binding fails for all other properties when I include this partial-view but works when I comment it out. I'm baffled and would greatly appreciate any help you can afford me.
<section id="widget-grid" class="">
<div class="row contactContainer">
#{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel()); }
</div>
</section>
This is the controller action method I'm trying to bind posted data to:
[Route("[action]"), HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public IActionResult Register([FromForm] EmployeeViewModel model, [FromQuery] string returnUrl = null)
{
if (ModelState.IsValid)
{
}
return View(model);
}
In order to bind, the input names much follow a particular convention that maps to what you're binding to. While it's unclear from your question, my best guess is that you're trying to ultimately bind to an instance of EmployeeViewModel, which means that your contact information inputs would need names like: EmployeeContact[0].Telephone, but when you pass an instance of ContactDetailViewModel along as the "model" of the partial view, the names will be just Telephone, and worse, these same names will be repeated over and over, i.e. each contact information set of fields you create will all have an input named just Telephone.
Long and short, you need the context of the whole model to generate the correct input names. You have a couple of options.
Since you're retrieving the set of fields via an AJAX request, it would be possible to pass the "prefix" to use along with that request. In other words, you can keep track of an index value, counting how many of these sections you've added, and then send along with the request for a new section something like
prefix: 'EmployeeContact[' + (i + 1) + ']',
Then, in your partial view:
#{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel(), new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = ViewBag.Prefix } } ); }
That's a little hacky, and honestly probably rather prone to error, though. The better option would be to take an entirely different approach. Instead of calling back to get the partial view, define it just once as a template:
<script type="text/html" id="ContactInformationTemplate">
<!-- HTML for contact information inputs -->
</script>
Then, using a library like Vue, React, Angular, etc., you can set up a "foreach" construction tied to a particular JavaScript array which uses this template to render items in that array. Then, adding a new set of inputs is as simple as adding a new item to the array. You will have to do some works to customize the input names based on the index of the item in the array, but all of these client-side frameworks have ways to do that. That would also have the side benefit of not having to make an AJAX request every time you want to add a new section.

Pattern for passing common data to _layout.cshtml in MVC4.5

I am trying to come up with the best pattern for passing data to my _layout.cshtml page.
I am toying with creating a common base class from which all view specific models derive. This base class would be recognized by my _layout.cshtml and used to fill in details about the user and load proper images in the header, etc. For example, here is a snippet of it.
public abstract class ViewModelBase
{
public string Username { get; set; }
public string Version { get; set; }
}
At the top of my _layout.cshtml I have...
#model MyProject.Web.Controllers.ViewModelBase
I need a common area to hydrate the information required by the model, and am planning to use the following pattern...
Each action method creates and hydrates a model derived from
ViewModelBase.
The action completes.
I create a ActionFilterAttribute and override OnActionExecuted to cast the
current Result to ViewModelBase.
If the conversion is successful, then I populate the ViewModelBase details with the relevant data.
Here are my questions...
Is the use of a ActionFilterAttribute (OnActionExecuted) a good pattern for what I am trying to do?
I am not able to see how to get the Result created in the action from the HttpActionExecutedContext. How is this done?
I follow the same approach and use a base ViewModel class which all my other viewModels inherit from.
Then, I have a base controller that all controller inherit from. In there, I have one method that takes care of initializing the view model:
protected T CreateViewModel<T>() where T : ViewModel.BaseViewModel, new()
{
var viewModelT = new T {
HeaderTitle = "Welcome to my domain",
VisitorUsername = this.VisitorUsername,
IsCurrentVisitorAuthenticated = this.IsCurrentVisitorAuthenticated,
//...
};
return viewModelT;
}
Then on each controller, when I want to create the view model, I simply call the base controller's method:
var vm = base.CreateViewModel<MyPageCustomViewModel>();

Same view for both create and edit in MVC4

Can we have a single razor view for both Create and Edit operations?
If yes, how do we achieve this?
I don't recommend it.
This should be a rather long answer, because there's a lot of things involved in the process, request and workflow of a normal MVC GET/POST workflow. I will try to answer your question with the minimum information required and why I do not recommend the use of the same view.
First, why?
You don't have control over the views, which may have over-posting;
No flexibility;
Not reusable views or parts;
Hard to maintain the views (one change in the view must be tested on both actions).
My suggested approach would be to have different actions/views but share common code:
Create both views as normal.
You will have duplicated code, but not all code is the same, for example, you may not want to send an ID on the create action, this is not directly related to your question, but using the same view implies you are also sending the same data, and this is not recommended, especially for over-posting or mass assignment. More info about mass assignment here (an Architectural Approach is what I'm using here).
So let's start from what are you going to receive in your controllers.
In this case I used inheritance but it's not the only strategy.
Binding models
public class UpdateBindingModel : CreateBindingModel {
// since we are not using the same binding model,
// we can have a "real" validation rules on our update binding and view.
[Required]
public int? Id {get;set;}
}
public class CreateBindingModel {
// no id here prevent overposting.
[Required]
public string Name {get;set;}
[Required]
public int? CountryId {get;set;}
}
That will make sure the data you send to your Create and Edit is the minimum needed and nothing else.
Let's then see the View Models that will be sent to the View, for this example I will include a List that will be used to select some value but should not be posted (the list) to the controller, only the selected value.
View models
public class CreateViewModel : CreateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
public class UpdateViewModel : UpdateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
As you can see, this gives you lot of flexibility but still have some duplicated code (the extra information needed on view model for both views) which can be mitigated in several ways (depending the needs/context):
Have an action to retrieve the common data and using #Html.Action("GetCountryList");
Use the same View Model aka CreateUpdateViewModel and discarding extra UpdateBindingModel properties in the view but still posting the corresponding model on POST.
Having your binding models as properties and select one or the other in the specific view. (better use #Html.EditorFor instead of partials so Model Binder will work with no additional change on code)
The controller actions will look like:
Controller
[HttpGet]
public ActionResult Create(){
ViewData.Model = new CreateViewModel();
return View();
}
[HttpPost]
public RedirectToRouteResult Create(CreateBindingModel binding) {
// check valid model state and create data
return RedirectToAction("Index");
}
[HttpGet]
public ActionResult Update(int id) {
var objectToEdit = service.GetObjectToEdit(id);
ViewData.Model = new UpdateViewModel(objectToEdit);
return View();
}
[HttpPost]
public RedirectToRouteResult Update(UpdateBindingModel binding) {
// check valid model state and update data
return RedirectToAction("Index");
}
And your views:
Views
Update.cshtml
<form action="Update">
#Html.HiddenFor(Model.Id);
#Html.Partial("EditFieldsPartial")
<button>delete</button> // no delete button on create.
<button>create new</button> // you can have a create new instead of update.
</form>
Create.cshtml
<form action="Create">
#Html.Partial("EditFieldsPartial")
</form>
Note: code is incomplete and didn't use helpers in most cases for brevity and clarity. Do NOT copy paste :D
Sure you can.
On post, check in your controller whether the primary key has value 0 then Insert, otherwise Update.
View should be the same for Create and Edit.
Just remember to include:
#Html.HiddenFor(model=>model.ID)
In your view
For example:
Model:
public class DescriptionModel
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
}
CreateEdit.cshtml:
#model DescriptionModel
#using (Html.BeginForm("CreateEdit"))
{
#Html.HiddenFor(model=> model.ID)
#Html.EditorFor(model=> model.Description)
<input type="submit" value='Submit' />
}
DescriptionModel controller:
public ActionResult Create()
{
return View("CreateEdit", new DescriptionModel());
}
public ActionResult Edit(int id)
{
return View("CreateEdit", db.DescriptionModels.Find(id));
}
// Submit and add or update database
[HttpPost]
public ActionResult CreateEdit(DescriptionModel model)
{
if (ModelState.IsValid)
{
// No id so we add it to database
if (model.ID <= 0)
{
db.DescriptionModels.Add(model);
}
// Has Id, therefore it's in database so we update
else
{
db.Entry(model).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
A View can definitely be shared for create and edit operations, using the same model. However, i would strongly recommend to think about it twice. In many cases, you will want to have a different view for edit operations(eg. hide some inputs that should not be editible) as well as the model could be slightly different, altought it might share some (or most) values. These difference will lead to some conditions in the view, checking whether you are creating or editing - which could make the code chaotic.
Conclusion: before deciding whether to have a shared view, try to think of how much is the edit screen gonna differ from create screen, then you may decide.
You certainly can, but usually that's something I will try to avoid. If the create and edit actions are virtually the same then you end up duplicating a lot of code in the controller. Usually in this situation I will have only a few fields on my 'Add' controller, and then once the item has been added I redirect the user to the edit page where they can fill in the rest of the information.
I wouldn't recommend that approach but you could have the main form be loaded into both views from a partial
[HttpGet]
public ActionResult myFun(int id = 0)
{
MyClass cls = new MyClass();
if (id == 0)
{
//Insert mode ... no data will be shown to textboxes , when primary key ie. id=0
//Display whole data
}
else
{
//Update mode... if id is not 0 ,data will be shown to textboxes
}
return View(cls);
}

MVC 4 How to process a url parameter on every page, base controller?

Looking for some guidance in designing my new MVC 4 app.
I would like to have a url parameter s=2011 on every page of the app to let me know what year of data I'm working with. Obviously, the user will have a way to change that parameter as needed.
I will need that parameter in every controller and wondering the best way to do this. I was thinking of creating a base controller that reads Request.QueryString and puts the year into a public property. However, considering all the extensability points in MVC, I'm wondering if there's a better way to do this?
This very much depends on the design of your app, but just to give you two alternatives
IActionFilter
If you are doing data context per request you can use a global IActionFilter to hook pre-action execution globally and apply a query filter to your data context behind the scenes.
Major down-side of this is that to test the controller you will need to have the full MVC pipeline setup so that the actionfilter gets applied properly.
Dependency Injection
Instead of using sub-classing (base controller as you say) you can use dependency injection . Keeping things more loose will allow you to pull the filter from query string, cookie, user setting in the database or whatever else - without your controller knowing where it comes from.
Here is some pseudo code how I would do it if I was using something like Entity Framework or Nhibernate (also I am sure applicable with other technologies as well)
public Car
{
public string Year { get; set; }
}
public class CarsDataContext : DbContext
{
private IQuerable<Cars> _cars = null;
private Func<Car, bool> _carsFilter = null;
public IQuerable<Car> Cars {
get {
if (_carsFitler != null)
return _cars.Where(_carsFitler);
return _cars;
}
set { _cars = value; }
}
public void ApplyCarsFilter(Func<Car, bool> predicate)
{
_carsFilter = predicate;
}
}
Assuming you have dependency injection setup already (NInject or whichever other framework) in you can configure how the context to be intialized
Bind<CarsDataContext>().ToMethod(() => {
string yearFilter = GetYearFilter(); // can be coming from anywhere
CarsDataContext dataContext = new CarsDataContext();
dataContext.Applyfilter(car => car.Year == yearFilter);
return dataContext;
}).InRequestScope();
Then my controller knows nothing about the data filtering and I can easily test it:
class MyController : Controller
{
public MyController(CarsDataContext dataContext)
{
}
...
}
However I would only do this is filtering the dataset was across many controllers and important part of my software. Otherwise it's pure over-engineering.

DDD confused with Repository Pattern and Reports

I am new to DDD and the Repository pattern, so my understanding of it might be totally wrong. But I am trying to learn it. Having said that I need to create an application, which shows the zones of a store. I create a ZoneRepository for that purpose, which works so far with my few methods. Now in that application I also need to show the distinct styles for that store. The list of styles will be used to drag them into the individual zones. Now my question is where does the styles class belong to, since its kind of a mini-report. Does that "StyleReport" belong into the repository? Does it belong somewhere else? How you know where it belongs to? Please help me to understand.
Repositories only act on Aggregate Roots. Aggregates are a boundary around one or more objects that are treated as a unit. By that I mean, when you operate on that data (inserting, updating, deleting, etc.), all of the objects within that boundary are affected accordingly. Every aggregate has a root. This root is what is referenced externally by other parts of the software. I guess one way to describe it is "something that doesn't rely on something else".
It's a little challenging to derive the proper definition of your domain from a description of your existing models. Furthermore, the design should be based on the business model and needs, not how your UI or application works. So, you should model it on the general problem you are solving, not on how you think you'd like to solve it.
It sounds like you have an entity Store. A Store can be divided up into one or more Zones. Each Zone then has one or more StyleReports. It sounds to me like the Zones are dependent on a Store, so the Store is the aggregate root. Now, perhaps these StyleReport entities are a global set of objects that you offer in your problem domain (meaning you define the StyleReports separately, application-wide, and refer to them in your Zones). In that case, perhaps StyleReport is also an aggregate root.
Here are some example models (C#, not sure what language you're using). However, don't take this as the absolute word. If I don't know the specifics about your domain, I can't very well model it.
public class Store
{
public Int32 ID { get; }
public String Name { get; set; }
public IList<Zone> Zones { get; private set; }
public Store()
{
Zones = new List<Zone>();
}
public void AddZone(Zone zone)
{
Zones.Add(zone);
}
}
public class Zone
{
public Int32 ID { get; }
public String Name { get; set; }
public IList<StyleReport> Styles { get; private set; }
public Zone()
{
Styles = new List<StyleReport>();
}
public void AddStyle(StyleReport style)
{
Styles.Add(style);
}
}
public class StoreRepository : Repository<Store>
{
public Store Get(Int32 id)
{
// get store from persistence layer
}
// find, delete, save, update, etc.
}
public class StyleReportRepository : Repository<StyleReport>
{
public StyleReport Get(Int32 id)
{
// get style from persistence layer
}
// find, delete, save, update, etc.
}
And so when modifying a store's zones and adding styles, maybe something like this
IRepository<Store> storeRepository = new StoreRepository();
IRepository<StyleReport> stylesRepository = new StyleReportRepository();
Store store = storeRepository.Get(storeID); // store id selected from UI or whatever
// add a zone to the store
Zone someZone = new Zone { Name = zoneNamea }; // zone name was entered by the UI
someZone.AddStyle(styleRepository.Get(styleID)); // style id was selected from UI
storeRepository.Update(store);