Auto-populating Select Boxes using jQuery & AJAX in asp.net MVC - c#

1-problem: I need to enable users to select one or more things from a large amount of information that is grouped into a hierarchical structure for selection, data entry, were data could have a depth of 4, 5 parent categories.
2-functionality I´m looking for:
Similar to eBay shows cascading lists when selecting an item’s category. When the page is displayed, you only get the first list box. After selecting one in the first, the second is displayed. The process continues until the selected category does not have sub-categories.
}
3-actual table and query:
table:
-int Id
-string Name
-int ParentId
query:
public IList<CategoryTable> listcategories(int parentId)
{
var query = from c in categorytable
where c.ParentId == parentId
select c;
var result= query.ToList();
return result;
}
4-I dont know how to start, any guideline, live example jsfiddle, demo or tutorial would be greatly appreciated.
brgds
UPDATE: I believe that this functionality is not very developed in webtutorials and questions. consequently I started a bounty for a great answer. I will asign the bounty for a live example of the functionality previous commented. thanks!

What I have learned by handling large amounts of data is:
don't try to load all data at once to the client
load only the data the client actually needs
do the filtering, searching and sorting in the database, e.g. by stored procedures. Especially for data, which are distributed across multiple tables.
optimize your database queries, indexes are good
keep always in mind how many simultanious queries do you expect
linq is good but not for everything when handling large data
spend time to think and plan what data are really needed
To display the data on your webpage there many jQuery plugins to list data where you could bind functions to an "selected"-event. For example knockOut.js, which comes with MVC4. You may don't need a fully loaded jQuery "hierachical-data-list-display"-plugin. Perhaps you can realize it by using "seleted"-events, ajax loading and show/hide functions.
According to your comments I would think of a combination of jQuery and MVC:
in MVC I would create a patial view like
#model MvcApplication.Models.DataModel
<ol id="#Model.DataCategorieLevel">
#for (var i = 0; Model.Data.Count > i; i++)
{
<li value="#Model.Data[i].ItemId" onclick="itemSelected(#Model.Data[i].ItemId, #Model.DataCategoryLevel);" >#Model.Data[i].ItemName</li>
}
</ol>
the javascript could be something like:
function itemSelected(selectedItemId, itemCategoryLevel) {
// ajax call to an action which loads the next categorie items into the partial view and returns them
// on success remove all lists with an category - level lower than itemCategoryLevel
// append the returned List to the HTML-container which holds the lists
}
in the called MVC-Action I would determine if it is the last category level or not. If it is the last level, I would return a different partial view with other onclick event bindings
This is what I would try to realize, before I start searching for some plugins

I'm using knockout and Webapi to power cascading dropdowns in an app I'm developing at the moment.
View
I've got a basic dropdown list like below.
<select data-bind="options: CurrentList,
optionsText: 'name',
value: CurrentListSelectedItem,
optionsCaption: 'Please Select...'"></select>
View Model
self.CurrentList = ko.observableArray(CurrentListData);
self.CurrentListSelectedItem = ko.observable();
self.CurrentListSelectedItem.subscribe(function () {
//ajaxcall to populate list 2
});
Server side I've got a simple rest service that take an Id of a point in the tree and returns all its children, this way you can just chain as many of these dropdowns together as you wish (as long as your hierarchy has the levels to match.
See fiddle of working example with mocked data http://jsfiddle.net/tgriley1/vEBGS/

I recently had a similar problem when using Cascading Drop-downs and I did something like this.
Firstly, write some jquery on the view so that when you select the first item it sends an ajax request to the server, and brings back a JSON or xml response.
I did something like
<script>
$(function () {
$("select#ParentId").change(function (evt) {
$.ajax({
url: "/Home/GetChildItems",
type: 'Post',
data: { ParentId: $("select#ParentId").val() },
success: function (data) {
var items = "";
$.each(data, function (i, val) {
items += "<option value='" + val.ChildId + "'>" + val.ChildName + "</option>";
});
$("select#ChildDropDown").empty().html(items);
}
});
});
});
</script>
On the Controller, something like
Public JsonResult GetChildItems(int ParentId)
{
//code to retrieve the data
JsonResult result = new JsonResult();
result.Data = **object that contains the child data**;
return result;
}
I'm a beginner myself, so I'm not sure how good this code is, but it worked for me when creating cascading drop-downs using jquery.
Hope it helps.
Link to the cascading drop down question : Populating dropdown with JSON result - Cascading DropDown using MVC3, JQuery, Ajax, JSON

Hi I had the same scenario , What I used is, a autocomplete list with with web API, after specific number of characters , it calls the Web API and loads the data for the particular wild card.
Apart from this when I found that data returned is still large , I added pagination at SQL server end

The telerik demo is always a good place to learn MVC from
http://demos.telerik.com/aspnet-mvc/razor/combobox/cascadingcombobox
This does not exactly use listboxes as per your screenshots but it could very easily be changed to use them. With a few javascript modifications you could have unlimited levels.
Here is another one:
http://weblogs.asp.net/raduenuca/archive/2011/04/03/asp-net-mvc-cascading-dropdown-lists-tutorial-part-5-1-cascading-using-jquery-ajax-ajax-and-dom-objects.aspx

Related

Cache ASP.Net MVC Page Data

I have an ASP.Net MVC 3 web application, and within one particular page there is quite a bit of data processing happening when the page loads. Please see my sample code below.
Basically the code is querying my database and pulling back data into the variable shiftDates, this could be anything up to a thousand records. Next I perform a group by and distinct on that data and place it into a variable called groupOrgs . Then I iterate through that data, and place each record and its related data into a ViewModel which my Razor View requires.
You may read this and think that this code shouldn't take too long to load, query and display to the user, however, there are two other pieces of code, very similar to that below, also within my Controller method which also run when the user loads the page. Therefore, picture the code below being asked to run three times within one page load. This then causes a noticeable delay on the page load time.
I guess I would like any help or advice on how to restructure my code below or that when the page loads, initially it pulls it from the database and does all the querying etc, but if the page is reloaded, then the data is pulled from a cached source.
Really, any help with this is greatly appreciated.
Thanks.
Controller
public ActionResult Index()
{
ViewModelHomepage model = new ViewModelHomepage();
IList<ViewModelHomepageCounters> orgShiftDates = new List<ViewModelHomepageCounters>();
//Get all available dates
IList<ShiftDate> shiftDates = _shiftDateService.GetAllShiftDatesToBeFilled();
//Get total dates per Organisation
var groupOrgs = shiftDates
.GroupBy(x => x.Shift.Organisation.description)
.Select(x => new
{
_id = x.FirstOrDefault().Shift.organisationID,
_orgName = x.Key,
_shiftDateCount = x.Count()
}
).OrderBy(x => x._orgName);
foreach (var item in groupOrgs )
{
ViewModelHomepageCounters t = new ViewModelHomepageCounters();
t.id = item._id;
t.OrgName = item._orgName.ToString();
t.totalShiftDates = Convert.ToInt32(item._shiftDateCount);
orgShiftDates.Add(t);
}
model.OrgShiftDates = orgShiftDates;
}

Best way to display profiles results in MVC in a paged way

I'm hesitating and thinking about how to display the result searches.
The situation is following:
I have a website, where a user can fill in few texboxes and based on that perform a search. And then i would like to display him the results in a paged way: something like:
photo, name, age | photo, name, age | photo, name age
So i would like to have a 3 columns, and then probably 5 rows.
But I don't know, what is the best way to represent something like this, is there a best approach etc?
Thanks in advance
Well, there's so many approaches you can go with from many different perspectives. We could really be talking about it for ages. But, in a nutshell, you can implement paging at the presentation layer, at the controller level, at the business logic layer or at the data layer. Basically, the lower you go down your application layer the better performance you'll get, there's no much difference between implementing paging at the controller and the business logic layer when it comes to performance but design wise it's better to keep these concerns at the business logic layer for better maintainability and scalability. You will get much better performance if paging is implemented at the data layer, specially if you have a lot of data to display and then tell the data access layer to fetch just the page of data your application is interested in. Using other approaches will force you to retrieve all data from the backend store which might result in unnecessary bandwidth consumption and data transfers. See this example below where I'm doing the paging at the data layer level..
Example
SQL Query
I'm using a custom data access layer that runs the sql query below to query a large amount of devices...
select top (#pagesize) *
from
(
select row_number() over(order by d.Name) as RowId
, d.Id
, d.Name
, d.IsDeleted
, dt.Id as DeviceTypeId
, dt.Name as DeviceTypeName
from Devices d
left join DeviceTypes dt on dt.Id = d.DeviceTypeId
)
as o
where o.RowId <= (#page * #pagesize) and o.RowId > ((#page - 1) * #pagesize)
The query is simple you specify the page size(records per data page) and the data page to retrieve (first page, the second page, etc.). This query is run by my data access layer which constructs business ojects and pass them to the Business Logic Layer
Business Logic
Then, in the my business logic I called the devices data object to retrieve the data page I'm interested in. The following method does that job...
public IList<DeviceBO> GetAllDevices(int page, out int count)
{
count = DataProvider.Devices.GetAllDevicesCount();
return DataProvider.Devices.GetAllDevices(page, BusinessConstants.GRID_PAGE_SIZE);
}
Very straight-forward operation. Yet, there's a few things to notice. One, that I'm making two round-trips to the database; one to get the count of all devices and the second one to retrieve a data page of devices. We can argue that this could be avoided which is true by doing a single trip to the database, but in my case it is a very quick query that runs in less than 1 second and believe me...I have over a million records. The call to the GetAllDevicesCount does nothing more than a sql select count(Id) from dbo.Devices
Also notice that DataProvider is just a factory object. You don't really need to worry about the implementation details. And BusinessConstants.GRID_PAGE_SIZE simple returns a number, the standard page size used across my application (I've got a few grids/tables) and it's just a way to keep things in one place in case I want to change the page size across all grids/tables later on.
Controller
Then I have defined a device controller that exposes the following action method (Manage)...
public class DevicesController : Controller
{
public ActionResult Manage(string currentPage)
{
return process_device_list(currentPage);
}
//this method was created as a result of code re-factoring since it is used in several other action methods not shown here
private ActionResult process_device_list(string currentPage, bool redirect = false)
{
int count = 0;
int page = 1;
if (!int.TryParse(currentPage, out page))
{
if (!string.IsNullOrEmpty(currentPage))
return RedirectToAction("Manage");
page = 1;
}
var model = new DeviceManagementListModel();
model.Devices = BusinessFactory.DevicesLogic.GetAllDevices(page, out count);
model.ActualCount = count;
model.CurrentPage = page;
if (!redirect)
return View(model);
else
return RedirectToAction("Manage", new { #currentPage = currentPage });
}
}
View
The view is pretty much just HTML and razor syntax, there's nothing interesting going on there. Perhaps, the footer of the table/grid is where more interesting things happen since the paging markup is define there....
<tfoot>
<tr>
<td colspan="4">
<div class="pager">
#using (Html.BeginForm("Manage", "Devices", FormMethod.Get)){
#Html.TextBoxFor(x => x.CurrentPage, new { title = "Enter a page number to change the page results" })
<input type="submit" value="Go" title="Change the current page result" />
}
</div>
<div class="total">
#Model.ActualCount record(s) found
</div>
</td>
</tr>
</tfoot>
And this is how it looks...
Notice in the tfoot section of the table in my view that I have added a form element that makes a GET request to my action method Manage rather than a POST request. This is useful to provide a nice restful url where users can specified manually the page they're interested in...
I hope it helps you get some basic ideas

Asynchronous updating list of options for multi select MVC

I'm working on a form for user input, and one of the items (a multiple option select) has an inordinate amount of choices (~1600), so it's gotta get filtered down to be digestible. I've got 3 filter fields (dropdowns) that I'm requiring to have completed before I make an AJAX call back to the DB and get an updated list. It's similar to How to filter the options of a drop down list using another drop down list, however I also don't want to lose any items that were previously selected. Here's the signature for the function I've prototyped:
public JsonResult GetContentStandardsForUser(string type, string grade, string subject, List<SelectListItem> selected)
What I want is to return the new list of items (and not lose the ones that were already selected), and have the pick-list update.
What is this AJAX call going to look like (using jquery)? Should I just include the current selected values in my query, or can I pass the SelectListItems like I've written above?
After some thought about the fantasy football example I presented, I came up with a solution. I make two multi-selects, one of available, one of selected. Only the "selected" list gets bound to the model-- the available list is what gets updated as a result of the query.
If someone can come up with a single-select control solution, I'm still interested, but this is a good workaround for me, for now. The reason I was looking for a single-select solution was that I was already using this plugin (http://www.virtuosoft.eu/code/bootstrap-duallistbox/) to filter my selected/available lists.
ETA: I realized I can do this in a single listbox with jquery. Using the ID, loop through the options, if it's not selected, remove it. Then add all new options from the query. Voila!
ETA2: Now with code!
//Filter content standards
$("#csType, #csGrade, #csSubject").change(function(){
var type = $("#csType").val();
var grade = $("#csGrade").val();
var subject = $("#csSubject").val();
if(type != "" && grade != "" && subject != "")
{
$("#csList option:not(:selected)").remove();
var items="";
$.getJSON("#Url.Action("GetContentStandardsForUser","Summary")", {type:type, grade:grade, subject:subject} ,function (data) {
$.each(data,function(index,item){
items+="<option value='"+item.Value+"'>"+item.Text+"</option>"
});
$("#csList").append(items)
$("#csList").trigger('bootstrapduallistbox.refresh', true);
});
}
});
After binding the options, call following function.
$('#csList').multiselect('rebuild');

How to Prevent Drop down to show values that is in Database

I want to prevent the value of the dropdown list when it is already in the database file so that theres no multiple input in the Database ,
I hope someone can Help me with this problem ,
I'm using MVC 3 and Kendo UI
is that possible to handle it via Jquery or a Linq Statement in the Controller ?
Thanks :)
<span>
#Html.DropDownList("ddlDay", new SelectList(ViewBag.DayList, "ID", "Display_Value", PersonDay), "[Please Select]",
new Dictionary<string, object>
{
{"class","validate[required] inputLong"}
})
</span>
This Dropdown Show Monday to Sunday , i have 2 of this drop down that shows availability But i like to know that if i save Monday to Wednesday , Monday to Wednesday is removed on the two dropdowns ,
private void GetDayList()
{
var dayList = (from a in db.Lookups
where a.Domain == "DAYSTATUS"
select a).ToList();
ViewBag.DayList = dayList ;
}
You can solve this in many ways, it will depend on how your interaction is done.
You could use LINQ, adding a Where() or Except() statement to filter the full list:
var fromDayList = dayList.Where(day => day.Name != selectedFromDay).ToList();
// or if multiple selected days, assuming it is of the same type:
var validDayList = dayList.Except(selectedDayList).ToList();
If you allow them to change the selected days client side and are using AJAX (which is how you usually use Kendo UI) then you won't want to filter server side. Instead you should wrap your DayList in a kendo.DataSource and apply a filter based on selected days in your JavaScript. You haven't provided enough information for me to supply any sample code for this.

DevExpress MVC Gridview + LINQ

We have a DX gridview being rendered in a specifically designed view. We pass a predefined ViewModel object whose values are filled from a Linq-2-Entities query. The problem is that in our callback function the L2E query is actually executed on the DB before any filtering, sorting and paging has been performed by the DevExpress grid. IE.: (simplified example, in the actual situation we select data from several tables but still in a single linq query)
public ActionResult GridViewPartial(string myParameters)
{
var modelData = from s in db.myTable
select new { modelName = s.Name };
return PartialView("GridViewPartial", modelData);
}
In this situation the query is actually executed before the data has been passed to the View. Therefore it actually selects way too much data from the DB while the gridview only displays the selected page.
How would we have to modify the query so it only selects the data of the page the user has selected? IE. skip 10 rows and take 10 in the L2E query when the user selects page 2, instead of selecting 100000 rows (if there are that many in the table) and afterwards applying the filtering/sorting/paging, like in the described situation?
The MVC GridView Extension supports the so-called “server mode” functionally via the internal LinqServerModeDataSource object.
It requires the IQueryable object as a datasource:
Direct LINQ query:
http://www.devexpress.com/issue=Q333116
#Html.DevExpress().GridView(...).BindToLINQ(string.Emptry, string.Emptry, (s, e) => { e.KeyExpression = Key_Column_Here; e.QueryableSource = Linq_Query_Here; }
The Table/View from the LinqToX DataCotnext/Classes;
http://mvc.devexpress.com/GridView/DataBindingToLargeDatabase
#Html.DevExpress().GridView(...).BindToLINQ(Data_Context_Name_Here, Table_View_Name_Here).GetHtml()
It appears that the object type should be of System.Linq.IQueryable in order for DevExpress's gridview to effectively use its Linq DB commands. Inside your controller, build up your logic and pass your Linq query to the View:
System.Linq.IQueryable modelData = from s in db.myTable
select new { modelName = s.Name };
return PartialView("GridViewPartial", modelData);
Inside the Razor view, initiate the gridview with the command:
#model System.Linq.IQueryable
#Html.DevExpress().GridView(...).BindToLINQ((string)null, null, (s, e) => { e.KeyExpression = "Table_id"; e.QueryableSource = Model;})
I would implement the paging/sorting/filtering in the data access layer/level and return only what needs to be shown, because the Grid as you noticed can show the right page but this happens client side and everything is always loaded from the database, except if you use their XPO ORM (which I dont) and enable the grid server-mode (at least this is the concept in their windows forms and ASP.NET Grid).
the answer to your question is that you should design your LINQ queries to accept as parameters page size and page index and do a Take(pageSize) from the specific pageIndex you need. All of this can also be done in a stored procedure directly on the db.

Categories

Resources