How do you model form changes under Spring MVC? - model-view-controller

Say you're writing a web page for fruit vendors using Spring MVC's SimpleFormController, version 2.5.6. On this page the vendor can do simple things like change their name or their address. They can also change their inventory based on a drop down list filled with present inventory selections.
When this drop down list selection changes, the entire form changes to match the inventory of what has been selected. So one stock selection may have bananas and pears, another may have melons, blueberries and grapefruit.
Inside each inventory selection is a input field that needs to be propagated back to the database, for the sake of this example let's say that the user enters the number of fruit.
The way this is modeled in the database is that each Stock name is stored in a table, which has a one to many relationship with the contents of each stock, which would be the type of fruit in this example. Then the type of fruit has a one to many relationship with the quantity the vendor selects. Stock name and the type of fruit in each stock are stored in the database and are unchangeable by the user, with the connected fruit quantity table being editable.
My question is, how do you model the form described above in Spring MVC?
I've tried overriding the isFormChangeRequest and onFormChange to facilitate the form change, but I think I may be misunderstanding the intent of these methods. When I change my backing command object the next time the page is post it tries to bind the request into the form, which breaks if you adjust the size of the Stock array (say from 3 to 2, it will try and bind into the 3rd value, even if it is empty).

If you have a limited amount of different stocks, you can use different handler mappings for each one with a different backing model:
#RequestMapping(params="stock=example1")
ModelAndView handleExample1(#ModelAttribute("stock") ApplesOrangesPears stockObject)
#RequestMapping(params="stock=example2")
ModelAndView handleExample2(#ModelAttribute("stock") BananasPotatos stockObject)
But I guess that is not the case, there are a lot of different stock types and they are dynamic. In that case you can register custom property editor (#InitBinder), and determine dynamically the actual type of the backing object for the inventory, then validate, and convert to or from it explicitly.

What I ended up doing is firing a JavaScript event when the selection in the drop down is changed. This JavaScript (seen below) generates a URL based on the selection of the drop down and uses a location.replace to go to the new URL, which causes the controller to generate a new form.
Using this method over overriding the isFormChangeRequest and onFormChange has allowed me to avoid binding errors caused by left over post data.
function changeUrl(selectionValue) {
var param = getParams();
param["dropdownselection"] = selectionValue;
window.location.replace(getBaseUrl() + buildQueryString(param));
}
//taken from http://javascript.about.com/library/blqs1.htm
function getParams() {
var qsParm = new Array();
var query = window.location.search.substring(1);
var parms = query.split('&');
for (var i = 0; i < parms.length; i++) {
var pos = parms[i].indexOf('=');
if (pos > 0) {
var key = parms[i].substring(0,pos);
var val = parms[i].substring(pos+1);
qsParm[key] = val;
}
}
return qsParm;
}
function getBaseUrl() {
var url = document.location.toString();
if (url.indexOf('?') != -1) {
url = url.substring(0, url.indexOf('?'));
}
return url;
}
function buildQueryString(param) {
var queryString = "?";
for (var key in param) {
queryString += key + "=" + param[key] + "&";
}
//remove last "&"
return queryString.substring(0,queryString.length - 1);
}

Related

Dynamics 365 API link between ActivityPointer and activitytypecode global option set

I am reading data from the ActivityPointer entity in Dynamics 365 via the API and I want to link the activitytypecode field value to the activitypointer_activitytypecode global option set, which I believe is the correct one. However the values don't seem to match. In the ActivityPointer.activitytypecode field I have values such as:
phonecall
bulkoperation
email
appointment
task
But those values don't appear in the option set definition, using this query: GlobalOptionSetDefinitions(Name='activitypointer_activitytypecode')
The option set has the code values (e.g. 4202 for Email) and the different descriptions in all languages, but nothing matches back to the values on ActivityPointer
Optionset is just key value pairs (4202: Email and so on), If you want to get the formatted text value of optionset (Email, Fax, etc) from your web api query results - then you have to use activitytypecode#OData.Community.Display.V1.FormattedValue to get it. Read more
I recommend this article for complete understanding of CRM activities.
If you are looking for the code integer value in your resultset, that seems to be an issue and the result is not the expected one - old SO thread
The problem is that if you are reading activitytypecode in code, then you will know that you get a string value. This is the logical name of the activity entity, e.g. "email", "phonecall" etc.
If you look at the definition of activitytypecode in Power Apps then it shows it as "Entity name" (i.e. text) but using the classic solution editor it shows as the global activitypointer_activitytypecode option set, which contains values for "Email", "Phone Call" etc.
I am sure that there should be a simple way of converting from activitytypecode (i.e. entity name) to activitypointer_activitytypecode (i.e. option set), but I've yet to find it.
What I am doing is retrieving the global activitypointer_activitytypecode option set, so I have access to all of the text values. Then retrieve details about the entity indicated by activitytypecode, specifically what is of interesting is the display name. Then loop through the option set looking for a case-insensitive match on display name.
This is my C# code:
public int? GetActivityType(IOrganizationService service, string activityTypeCode)
{
// Get all activity types.
var optionSetRequest = new RetrieveOptionSetRequest()
{
Name = "activitypointer_activitytypecode"
};
var optionSetResponse = (RetrieveOptionSetResponse)service.Execute(optionSetRequest);
var optionSetMetadata = (OptionSetMetadata)optionSetResponse.OptionSetMetadata;
var optionValues = new Dictionary<string, int?>(StringComparer.OrdinalIgnoreCase);
foreach (var option in optionSetMetadata.Options)
{
foreach (var optionLabel in option.Label.LocalizedLabels)
{
optionValues[optionLabel.Label] = option.Value;
}
}
// Get the display name for the activity.
var retrieveEntityRequest = new RetrieveEntityRequest
{
EntityFilters = EntityFilters.Entity,
LogicalName = activityTypeCode
};
var retrieveEntityResponse = (RetrieveEntityResponse)service.Execute(retrieveEntityRequest);
LocalizedLabelCollection entityLabels = retrieveEntityResponse.EntityMetadata.DisplayName.LocalizedLabels;
// Look up the display name in the option set values.
foreach (var entityLabel in entityLabels)
{
if (optionValues.TryGetValue(entityLabel.Label, out int? value))
{
return (Schema.GlobalOptionSet.ActivityType?)value;
}
}
// If we get here then we've failed.
return null;
}
That is making two API calls, so best avoided in any situations where performance might be an issue. I'm not saying the code is perfect, but it hasn't let me down yet. Even so, I would recommend making do with the logical names provided by activitytypecode if you can.

Knockout Performance - Filtering an Observable Array

I'm new to Knockout, and I'm trying to use it on a page showing a system's users and the roles that each user has.
The data is in an observableArray of users. The user object has a roles property, which is another observableArray. This second array contains an object for each role, with an ID and a boolean "granted" property.
I want to be able to display all of the users with a specific role, so there's a checkbox for each role - when one of these is checked, the list should show the users with that role.
The problem I've got is that filtering the 1,000 or so users by roles takes several seconds. Filtering by the text in the name is very quick (a few milliseconds), but filtering by role is not. I've put some timing code in, and the issue is the method I'm using to check whether the user has the selected role(s) so I'm just wondering whether there's a better way of doing it, maybe using some Knockout magic.
Below is the ko.computed on the view model that I'm using to do the filtering. The results table is bound to this function.
self.filteredUsers = ko.computed(function () {
var textFilter = self.filter(); // this is an observable bound to a text field
var checkedRoles = self.selectedRoles(); // this is a computed, which returns an array of checked roles
return ko.utils.arrayFilter(self.users(), function (user) {
var match = true;
if (user.displayName.toLowerCase().indexOf(textFilter.toLowerCase()) == -1) {
match = false;
}
// for each ticked role, check the user has the role
for (var i = 0; i < checkedRoles.length; i++) {
var roleMatch = false;
for (var j = 0; j < user.roles().length; j++) {
if (user.roles()[j].roleId === checkedRoles[i].roleId && user.roles()[j].granted()) {
roleMatch = true;
break;
}
}
if (!roleMatch) {
match = false;
}
}
return match;
});
});
I think that a good optimization would be creating a grantedRoles computed on your user object. This computed would return an object that you can use as an index, would contain properties keyed by a role's unique identifier and would only contain roles that are granted.
Then in filteredUsers, you would check the grantedRoles object against each checked role, rather than looping through user.roles() for each checked role.

How do I improve query speed on a paged grid with a large number of results?

I am querying data from our IBM i and displaying it in a grid. The purpose of displaying all records is for a couple reasons:
The existing software isn't used properly and people aren't closing out the items. (user/training issue yes, but see other items). So narrowing down the list to just open items isn't accurate.
It allows a user to query all history (this is property based and history can be important)
However, there currently is 28,000 items and will ever increase. Right now, I am using MvcContrib grid. Here is my code:
public ActionResult Index(GridSortOptions gridSortOptions, int? page, int? filterPropertyUniqueKey, int? filterPermitNumber)
{
#region Filter and Sort
var permits = buildingPermitRepository.GetOpenPermits();
// Set default sort and apply filters
if (filterPermitNumber.HasValue)
{
permits = permits.Where(w => w.PermitId == filterPermitNumber.Value);
}
// TODO add more filters
if (String.IsNullOrEmpty(gridSortOptions.Column))
{
gridSortOptions.Column = "DateApplied";
gridSortOptions.Direction = SortDirection.Descending;
}
var permitsPagedList = permits.OrderBy(gridSortOptions.Column, gridSortOptions.Direction).AsPagination(page ?? 1, 20);
#endregion
var viewModel = new PermitIndexViewModel
{
BuildingPermits = permitsPagedList,
GridSortOptions = gridSortOptions
};
return View(viewModel);
}
What would you suggest I do differently to improve the display speed? At least for subsequent views.
I don't know how AsPagination method works, but we use Skip and Take methods.
So after all filtering is done your code could look like this:
var permitsPagedList = permits.OrderBy(gridSortOptions.Column, gridSortOptions.Direction).Skip(pageSize * (page -1)).Take(pageSize).ToList();
This simple method returns only those rows which we actualy needs.

Using JQGrid with custom paging in Asp.Net MVC

I am using JQGrid with the Trirand.Web.Mvc class, and trying to figure out how to do custom paging.
I have seen the paging demos here
The problem with these demos is that they bind directly to a linq context object and lets MVC take care of the paging.
// This method is called when the grid requests data. You can choose any method to call
// by setting the JQGrid.DataUrl property
public JsonResult PerformanceLinq_DataRequested()
{
// Get both the grid Model and the data Model
// The data model in our case is an autogenerated linq2sql database based on Northwind.
var gridModel = new OrdersJqGridModel();
var northWindModel = new NorthwindDataContext();
// return the result of the DataBind method, passing the datasource as a parameter
// jqGrid for ASP.NET MVC automatically takes care of paging, sorting, filtering/searching, etc
return gridModel.OrdersGrid.DataBind(northWindModel.OrdersLarges);
}
The data set I want to bind to is quite complex and I am returning it from a stored procedure, which does the paging for me.
So all I have to give JQGrid is the correct size of rows for a specific page of the entire resultset. I can also return the total row count.
So I have my results in a List myListOfObjects.
I can pass this into the DataBind using myListOfObjects.AsQueryable()
The problem is, JQGrid thinks there is only {page size} rows, so does not display any of the paging options.
Is it possible to pass in the total row count?
Other grids, like Teleriks MVC grid allows you to pass in the Total row count, and it displays the paging correctly
Ok, so I've managed to solve this myself. There may be other ways to do it, if so I'd love to hear them!
The JQGrid.DataBind produces an JsonResult object, whose Data value is set to Trirands own object Trirand.Web.Mvc.JsonResponse
It's an internal class to their Trirand.Web.Mvc, so i had to copy its structure which I could see using Visual Studio debugging.
It has:
page - the current page number
records - the total record count
rows - of type Trirand.Web.Mvc.JsonRow (which I need to replicate too)
total - the total number of pages needed
JsonRow looks like:
cell - a string array of your columns
id - your row ID
So my code looked like this:
var jsonList = new List<JSONRow>();
myData.ForEach(x => jsonList.Add(new JSONRow(x)));
var jsonResult = Json (new
{
page = page,
rows = jsonList.ToArray(),
records = totalRows,
total = Math.Round((double)totalRows / rows, MidpointRounding.AwayFromZero)
}, JsonRequestBehavior.AllowGet);
return jsonResult;
My JsonRow looks like this:
public class JSONRow
{
public string[] cell { get; set; }
public string id { get; set; }
public JSONRow(MyObjectType myObject)
{
id = myObject.id;
cell = new string[3];
cell[0] = myObject.Col1;
cell[1] = myObject.Col2?? "";
cell[2] = myObject.Col3?? "";
}
}

Using DataObjectTypeName in DataObjectSource

The functionality I am trying to use is:
- Create a ObjectDataSource for selection and updating controls on a web page (User Control).
- Use the DataObjectTypeName to have an object created that would send the data to an UpdateMethod.
- Before the values are populated in the DataObjectTypeName’s object, I would like to pre-populate the object so the unused items in the class are not defaulted to zeros and empty strings without me knowing whether the zero or default string was set by the user or by the application.
I cannot find a way to pre-populate the values (this was an issue back in 2006 with framework 2.0). One might ask “Why would anyone need to pre-populate the object?”. The simple answer is: I want to be able to randomly place controls on different User Controls and not have to be concerned with which UpdateMethod needs to handle which fields of an object.
For Example, let’s say I have a class (that reflects a SQL Table) that includes the fields: FirstName, LastName, Address, City, State, Zip. I may want to give the user the option to change the FirstName and LastName and not even see the Address, City, State, Zip (or vice-versa). I do not want to create two UpdateMethods where one handled FirstName and LastName and the other method handles the other fields. I am working with a Class of some 40+ columns from multiple tables and I may want some fields on one screen and not another and decide later to change those fields from one screen to another (which breaks my UpdateMethods without me knowing).
I hope I explained my issue well enough.
Thanks
This is hardly a solution to the problem, but it's my best stab at it.
I have a GridView with its DataSourceID set to an ObjectDataSource.
Whenever a row is updated, I want the property values in the object to be selectively updated - that is - only updated if they appear as columns in the GridView.
I've created the following extension:
public static class GridViewExtensions
{
public static void EnableLimitUpdateToGridViewColumns(this GridView gridView)
{
_gridView = gridView;
if (_gridView.DataSourceObject != null)
{
((ObjectDataSource)_gridView.DataSourceObject)
.Updating += new ObjectDataSourceMethodEventHandler(objectDataSource_Updating);
}
}
private static GridView _gridView;
private static void objectDataSource_Updating(object sender, ObjectDataSourceMethodEventArgs e)
{
var newObject = ((object)e.InputParameters[0]);
var oldObjects = ((ObjectDataSource)_gridView.DataSourceObject).Select().Cast<object>();
Type type = oldObjects.First().GetType();
object oldObject = null;
foreach (var obj in oldObjects)
{
if (type.GetProperty(_gridView.DataKeyNames.First()).GetValue(obj, null).ToString() ==
type.GetProperty(_gridView.DataKeyNames.First()).GetValue(newObject, null).ToString())
{
oldObject = obj;
break;
}
}
if (oldObject == null) return;
var dynamicColumns = _gridView.Columns.OfType<DynamicField>();
foreach (var property in type.GetProperties())
{
if (dynamicColumns.Where(c => c.DataField == property.Name).Count() == 0)
{
property.SetValue(newObject, property.GetValue(oldObject, null), null);
}
}
}
}
And in the Page_Init event of my page, I apply it to the GridView, like so:
protected void Page_Init()
{
GridView1.EnableLimitUpdateToGridViewColumns();
}
This is working well for me at the moment.
You could probably apply similar logic to other controls, e.g. ListView or DetailsView.
I'm currently scratching my head to think of a way this can be done in a rendering-agnostic manner - i.e. without having to know about the rendering control being used.
I hope this ends up as a normal feature of the GridView or ObjectDataSource control rather than having to hack it.

Resources