MVC - Posting From a Partial View - asp.net-mvc

I have an MVC web application and I'm using a partial view. This consists of a series of criteria and associated checkboxes.
Shown here
Currently a checkbox is checked and the save button clicked, posting via this custom action
[HttpPost]
public PartialViewResult SaveParticipationMeasurementTimeCriteria(int participationMeasurementTimeCriteriaId, bool response)
{
var participationMeasurementTime =
repository.GetParticipationMeasurementTime(participationMeasurementTimeCriteriaId);
repository.SaveParticipationMeasurementTimeCriteria(participationMeasurementTimeCriteriaId, response);
unitOfWork.Save();
var vm = GetParticipationMeasurementTimeCriteriaVM(participationMeasurementTime.ParticipationMeasurementTimeId);
return PartialView("ParticipationMeasurementTimesCriteria", vm);
}
Which routes to this method in the repository:
public void SaveParticipationMeasurementTimeCriteria(int participationMeasurementTimeCriteriaId, bool response)
{
var participationMeasurementTimeCriteria = Context.ParticipationMeasurementTimeCriteria
.First(x => x.ParticipationMeasurementTimeCriteriaId == participationMeasurementTimeCriteriaId);
participationMeasurementTimeCriteria.Response = response;
participationMeasurementTimeCriteria.ModifiedDate = DateTime.Now;
participationMeasurementTimeCriteria.ModifiedBy = System.Threading.Thread.CurrentPrincipal.Identity.Name;
}
My objective is to streamline the process by only having one save button that updates all of them. There could be any number of criteria.
I've tried using JQuery to fire the action when the checkbox is updated.
I've tried creating a new custom action in the controller to take a list of criteria and update them, as shown below.
[HttpPost]
public PartialViewResult SaveParticipationMeasurementTimeCriteria(List<ParticipationMeasurementTimeCriteria> participationMeasurementTimeCriterias, int participationMeasurementTimeCriteriaId)
{
var participationMeasurementTime =
repository.GetParticipationMeasurementTime(participationMeasurementTimeCriteriaId);
foreach (var criteria in participationMeasurementTimeCriterias)
{
var participationMeasurementTimeCriteria =
participationMeasurementTimeCriteriaRepository.GetById(criteria.ParticipationMeasurementTimeCriteriaId);
participationMeasurementTimeCriteriaRepository.Update(criteria);
}
unitOfWork.Save();
var vm = GetParticipationMeasurementTimeCriteriaVM(participationMeasurementTime.ParticipationMeasurementTimeId);
return PartialView("ParticipationMeasurementTimesCriteria", vm);
}
But now I'm at a loss. Clearly there are gaps in my knowledge pertaining both to partial views and mvc in general, in regards to posting a list of entities from a view.
Any help would be appreciated.

Related

Performance issue. Views with kendo Ui grid take longer to load. Any suggestions?

Scenario: I am using Kendo UI to my MVC application. I have created several partial views which contains Kendo Grid and renders these views in Index.cshtml page using Tabstrip. The sample code is as below. Application takes around 15-18 seconds first time launch. The network log (IE developer tool) suggest all these partial views methods takes around 1 second each. Any suggestion how can I improve the performance to load the application faster?
Main View - Index.cshtml
#(Html.Kendo().TabStrip()
.Name("Tabstrip_StrategyData")
.Items(tabstrip =>
{
tabstrip.Add().Text("suppliers")
.Content( #Html.Partial("Suppliers").ToHtmlString());
tabstrip.Add().Text("Productions")
.Selected(true)
.Content(#Html.Partial("Productions").ToHtmlString());
tabstrip.Add().Text("Sales")
.Content(#Html.Partial("Sales").ToHtmlString());
})
)
Controller methods
public ActionResult Suppliers_Read([DataSourceRequest] DataSourceRequest request)
{
return Json(StrategyDataRepository.Suppliers_Read().ToDataSourceResult(request));
}
public ActionResult Productions_Read([DataSourceRequest] DataSourceRequest request)
{
return Json(StrategyDataRepository.Productions_Read().ToDataSourceResult(request));
}
Repository methods
public IEnumerable<SuppliersViewModel> Suppliers_Read()
{
IEnumerable<SuppliersViewModel> obj = (from sd in dbContext.StrategyData
select new SuppliersViewModel
{
Id = sd.Id,
CountryName = sd.Name,
LocationName = sd.LocationName,
}).ToList();
return obj;
}
public IEnumerable<ProductionsViewModel> Productions_Read()
{
IEnumerable<ProductionsViewModel> obj = (from sd in dbContext.StrategyData
select new ProductionsViewModel
{ Id = sd.Id,
CountryName = sd.Name,
LocationName = sd.LocationName,
}).ToList();
return obj;
}
Any suggestion to improve the loading time?

View/Model data isn't refreshing/changing after post/postback, even though I'm using the PRG pattern

Update I have saved my problem a long time ago. The problem was that I was trying to call the view model on the wrong view method! I was calling the base view method (Document), instead of one of it's derived method (like NewDocument, PDFDocument, etc.) Thus it was only giving me the Documents data, which didn't change. I was looking and using the wrong view method all the time... Stephen, when you asked me
"Why do you create derived classes in a method but then return only the base class"
I couldn't answer the question at the time because I didn't even know myself, until I remember that originally, the method wasn't returning the base class. I only changed it so that it can work with the base view method, which was wrong in the first place!
That's what I get for only getting 3-4 hours of sleep in 3 days. Everything works right now. Thanks.
I'm having a hard time trying to figure out why the data in my view isn't changing after I do a post. Originally I was doing it via return View() and it worked, but since it was a partial view, the page didn't look great, so I was reading up and saw that it was better to do it by Post-Redirect-Get pattern (PRG) and to use an id value to retrieve the values instead of sending the entire model via Tempdata. I even used ModelState.Clear() and that didn't even work. When I debugged the code, the model only has the values from when I first called it.
Here's part of my Get controller:
NewDocument Get Controller
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// This doesn't work. The view keeps on showing the data from View(Services.CreateNewDocument()).
if (id != null)
{
return View(Services.GetdocumentViewModelData(DocEnum.Section.NEW_DOC_INDEX, (int)id));
}
// This works fine
return View(Services.CreateNewDocument());
}
And here's the post that calls the redirect:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
int? DocID = Services.AddingNewDocument(dvm);
// See, I even tried to clear it.
ModelState.Clear();
return base.RedirectToAction("NewDocument", new { id = DocID });
}
else
{
// Display errors in the modal
}
return base.RedirectToAction("NewDocument");
}
And here's the old way I did it:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
Services.AddingNewDocument(ref dvm);
dvm.NewRecordMode = DocEnum.Action.UPDATE;
// It worked, but only the partial view showed, and not the entire view.
return PartialView("_NewDocument", dvm);
}
else
{
// Display errors in the model
}
return base.RedirectToAction("NewDocument");
}
Could it be because I'm using a custom model binding?
My Custom Model Binding
public class BaseClassModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var modelType = bindingContext.ModelType;
var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ViewModel");
if (modelTypeValue == null)
throw new Exception("View does not contain the needed derived model type name");
var modelTypeName = modelTypeValue.AttemptedValue;
var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);
if (type == null)
{
throw new Exception(String.Format("Derived model type {0} not found", modelTypeName));
}
var instance = bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
return base.BindModel(controllerContext, bindingContext);
}
}
EDIT: And here's the GetDocumentViewModelData code:
GetDocumentFromViewModelData
public static DocumentViewModel GetDocumentViewModelData(DocEnum.Section docType, int id)
{
switch (docType)
{
case DocEnum.Section.NEW_DOCUMENT_INDEX:
// NewDocumentTypeViewModel is a child to DocumentTypeViewModel
DocumentTypeViewModel nd = NewDocumentService.GetViewModelByID(id);
return nd;
case DocEnum.Section.PDF_DOCUMENT:
DocumentTypeViewModel pdfvm = PDFDocumentService.GetViewModelByID(id);
return pdfvm;
case DocEnum.Section.XLS_DOCUMENT:
DocumentTypeViewModel xlsvm = XLSDocumentService.GetViewModelByID(id);
return xlsvm;
}
return null;
}
Edit: Also adding the GetViewModelByID function
GetViewModelByID
public static DocumentTypeViewModel GetViewModelByID(int id)
{
docEntities db = new docEntities();
NewDocumentTypeViewModel vm = new NewDocumentTypeViewModel();
// Calls a stored procedure called Select_Documents_ByID(id) to get the note entry
// that was submitted.
List<Select_Documents_ByID_Result> prevNotes = db.Select_Documents_ByID(id).ToList();
StringBuilder sNotes = new StringBuilder();
foreach (var note in prevNotes)
{
sNotes.AppendFormat("{0} - {1}: {2}\n\n", note.CreatedDate.ToString("yyyy-MM-dd HH:mm"), note.username, note.Entry);
}
vm.PreviousNotes = sNotes.ToString();
return vm;
}
Edit: I did a direct creation of the view model inside the Get controller, and it's the same result. when i debugged the view itself, the values from the new view model don't show up. Instead, the values from the initial view model, View(Services.CreateNewDocument()), shows.
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// Right here I created the view model to test thing, but I'm getting the same results. Nothing has changed.
if (id != null)
{
var d = new NewDocumentTypeViewModel(1, "Help!");
// This property is from the base class, DocumentTypeViewModel
d.DocumentTitle = "Testing!";
return View(d);
// Inside the view itself, none of the values in the view model, including the one
// belonging to the base class. It still shows the initial values.
}
// This works fine
// Or maybe not...
return View(Services.CreateNewDocument());
}
Edit: I wanted to see if it was also doing the same thing for the initial call to the view return View(Services.CreateNewDocument()), and decided to change the value for documentTitle in the base class from New Document to a randomly-generated number, after the object has been created.
Here's the code for DocumentTypeViewModel's default constructor:
public DocumentTypeViewModel()
{
DocumentTitle = "New Document";
NewRecordMode = DocEnum.Action.ADD;
DocumentID = 0;
}
And here's the Services.CreateNewDocument() code where I change the DocumentTitle after the View Model has been created.
public DocumentTypeViewModel CreateNewDocument()
{
DocumentTypeViewModel dtvm = new DocumentTypeViewModel();
Random r = new Random();
dtvm.DocumentTitle = r.Next(5, Int32.MaxValue).ToString();
return dtvm;
}
Now in the View, when I call DocumentTitle:
<div class="label-text-group">
#Html.LabelFor(model => model.DocumentTitle)
#Html.EditorFor(model => model.DocumentTitle)
</div>
You would expect to see a randomly-generated number every time the View gets called. Nope, what you would see is "New Document". Weird.
It's seems that Services.GetDocumentViewModelData() is not exactly working correctly. It only carries the values created by the base class' constructor when a view is created, not any values that have been added or changed within GetDocumentViewModelData() itself. Why is that? What's going on? Please help anybody!
I have solved it. Look at the Update section on top. Thanks Stephen.

How can I have an object which can be accessed from different controllers?

I'm working with ASP.Net MVC4, I customize my login, this is ok, I would like save this object USER and I can access from differents controllers and differents Areas. I was trying to defined this object as "static" but I can't acces to values of object:
if (Servicio.ValidarUsuario())
{
string Mensaje = "";
Models.AdmUsuario oAdmUsuario = new Models.AdmUsuario();
oAdmUsuario.Au_codusuario = login.UserName;
Servicio.RetornaEntidad<Models.AdmUsuario>(ref Mensaje, "admsis.adm_usuario", oAdmUsuario.getPk(), oAdmUsuario);
***Models.AdmUsuario.UserWeb = oAdmUsuario;***
FormsAuthentication.SetAuthCookie(login.UserName, false);
Session["Modulo"] = null;
Session["Menu"] = null;
return RedirectToAction("index", "raMainReclamo", new { area = "Reclamos" });
}
In the model I define:
public static AdmUsuario UserWeb;
But I can't access to value.
Do you have any idea, how I can to access the values ​​of an object from different controllers in different areas?
You need a way to store the object between requests. You could put the object in Session Memory and pull it back out.
{
// Other Code
Session["AdmUsuario"] = oAdmUsuario;
return RedirectToAction("index", "raMainReclamo", new { area = "Reclamos" });
}
Controller in Reclamos Area
public class raMainReclamoController : Controller
{
public ActionResult Index() {
var oAdmUsuario = Session["AdmUsuario"] as Models.AdmUsuario;
// Other Code
}
}
However, the a more standard approach would be to persist the object to a database and then pull it back out. You could read up on using Entity Framework to access a sql database. I like to use RavenDB for storage as it makes saving objects really easy.
** UPDATE IN RESPONSE TO COMMENTS **
This is just psuedo code as I don't know what you are using to connect to postgres.
{
// Other Code
oAdmUsuario = postgresContext.Store(oAdmUsuario);
postgresContext.SaveChanges();
return RedirectToAction("index", "raMainReclamo", new { area = "Reclamos", id = oAdmnUsuario.Id });
}
Controller in Reclamos Area
public class raMainReclamoController : Controller
{
public ActionResult Index(int id) {
var oAdmUsuario = postgresContext.GetById<Models.AdmUsuario>(id);
// Other Code
}

Controller searching for view instead of returning a different view method

I have two controller actions outlined below:
public ViewResult TitleStorylines(int id)
{
var vm = Service.Get(id);
vm.IsEditable = User.HasPermission(SecurityPermissionType.ManageStorylines);
return View(vm);
}
public ViewResult TitleStorylinesCreate(TitleStorylineModel model)
{
var created = Service.Create(model);
return TitleStorylines(created.TitleId);
}
I only have one view in my project, called TitleStorylines, which the first controller action handles fine. But when I call the second method, it gives me an error saying that it can't find the view called TitleStorylinesCreate even though I'm explicitly calling the previous method. What gives?
Did you try ?
return View("TitleStorylines",created.TitleId);
EDIT: Based on your update : I guess you are posting your form back the TitleStorylinesCreate. So probably after saving, dont you want to redirect the user back to the Get action of same ?
[HttpPost]
public ViewResult TitleStorylinesCreate(TitleStorylineModel model)
{
var created = Service.Create(model);
return RedirectToAction("TitleStorylines",new { #id=created.TitleId});
}
In the above example we are doing the PRG pattern. Post -> Redirect -> Get
After saving, we are redirecting them back to the first method. It will be a HTTP GET method.
public ActionResult TitleStorylinesCreate(TitleStorylineModel model)
{
var created = Service.Create(model);
return RedirectToAction("TitleStorylines",new { #id=created.TitleId});
}

get kendo grid filters to post with a button using KendoGridBinder

I have a kendo ui grid and would like there to be an export button so that when they export to a CSV they could have the same filtering that is on the grid shown.
I would like the post to accept the KendoGridRequest much like the
[HttpPost]
public JsonResult Grid(KendoGridRequest request)
{
var employees = GetEmployees();
var grid = new KendoGrid<Employee>(request, employees);
return Json(grid);
}
I would like to add an Export button that would use the same filters in the post so
the Export action could also accept the KendoGridRequest and would return a CSV file
[HttpPost]
public ActionResult Export(KendoGridRequest request)
{
var employees = GetEmployees();
var grid = new KendoGrid<Employee>(request, employees);
return ToCsv(grid);
}
the KendoGridRequest class comes from the KendoGridBinder project
Basically you can find all the items (needed to create an KendoGridRequest object) contained in the dataSource field exposed by the object of the Grid and send them when posting your data to the Export action method.
i.e.
var dataSource = $('#yourGridID').data('kendoGrid').dataSource;
var page = dataSource._page;
var skip = dataSource._skip;
var take = dataSource._take;
//etc.

Resources