Log in to like this post! Creating Dashboards with NetAdvantage for jQuery and ASP.Net MVC 4 [Infragistics] Mihail Mateev / Monday, June 11, 2012 Developers always want to follow the latest technologies. Microsoft announced in June ASP.Net MVC 4 RC. Perhaps many of you are asking: how new versions of ASP.Net MVC work with existing components. This blog is a quick review how you can use Infragistics jQuery controls with MVC 4. The latest NetAdvantage for jQuery Vol. 12.1 includes many new exciting components and many feature enhancements to existing controls. You could find a detailed information about the existing controls in the Infragistics jQuery blogs. One of the advantages to using nice client components as those in NetAdvantage for jQuery is the ability to create quickly beautiful dashboards in your WEB applications using pre-defined styles and themes. ASP.Net MVC 4 offers better UI and possibility to easily create applications in Metro Style. Before to start: You need to install ASP.Net MVC 4 RC and NetAdvantage for jQuery Vol. 12.1. This demo is created with Visual Studio 2010Sp1. It is possible to create the project to create a similar manner and with Visual Studio 2012 RC Scripts and styles references In ASP.Net MVC4 you could place Infragistics css and js files in the same place like in ASP.Net MVC 3 projects.Infragistics recommends : styles to be placed under /Content/Infragistics folder JavaScript files location is under /Scripts/Infragistics folder You can use another location and change the path to your files ASP.Net MVC4 – Bundling One of the handy new features in ASP.NET MVC 4 is the ability to easily bundle your JavaScript and CSS. What means Bundling: this is the process of combining all of your disparate js and css files into one file (one for js and one for css).When you create a new ASP.NET MVC 4 application, you will find this code in the Application_Start() event in the Global.asax: 1: protected void Application_Start() 2: { 3: .... 4: BundleConfig.RegisterBundles(BundleTable.Bundles); 5: } You could configure your bundle using BundleConfig,cs The code below demonstrates you you could add to your bundle Infragistics script loader. 1: public class BundleConfig 2: { 3: public static void RegisterBundles(BundleCollection bundles) 4: { 5: bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 6: "~/Scripts/jquery-1.*")); 7: 8: bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include( 9: "~/Scripts/jquery-ui*")); 10: 11: bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 12: "~/Scripts/jquery.unobtrusive*", 13: "~/Scripts/jquery.validate*")); 14: 15: bundles.Add(new ScriptBundle("~/bundles/infragistics.loader").Include( 16: "~/Scripts/Infragistics/js/infragistics.loader*")); 17: 18: bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 19: "~/Scripts/modernizr-*")); 20: 21: bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css")); 22: 23: bundles.Add(new StyleBundle("~/Content/themes/base/css").Include( 24: "~/Content/themes/base/jquery.ui.core.css", 25: "~/Content/themes/base/jquery.ui.resizable.css", 26: "~/Content/themes/base/jquery.ui.selectable.css", 27: "~/Content/themes/base/jquery.ui.accordion.css", 28: "~/Content/themes/base/jquery.ui.autocomplete.css", 29: "~/Content/themes/base/jquery.ui.button.css", 30: "~/Content/themes/base/jquery.ui.dialog.css", 31: "~/Content/themes/base/jquery.ui.slider.css", 32: "~/Content/themes/base/jquery.ui.tabs.css", 33: "~/Content/themes/base/jquery.ui.datepicker.css", 34: "~/Content/themes/base/jquery.ui.progressbar.css", 35: "~/Content/themes/base/jquery.ui.theme.css")); 36: } 37: } If you prefer to add script and styles references without bundling it is possible to use the script and styles references in the same way like in ASP.Net MVC 3. If you refer styles and scripts you need to know that the reference path starts from the site root – not from the current document location: /Scripts/Infragistics/js/[my resource] – MVC 4 instead ../../Scripts/Infragistics/js/[my resource] – MVC 3 If you want to use MVC wrappers there is another step: MVC wrappers reference In ASP.Net MVC 4 it is possible to use libraries, build against MVC 3. You can add reference to: [NetAdvantage for jQuery install path]\MVC\MVC3\Bin\Infragistics.Web.Mvc.dll Application design The sample application is an ASP.Net MVC 4 Application, that uses Northwind sample database via Entity Framework. In the sample are used Infragistics jQuery Grid, Chart and Map (igGrid, igDataChart and igMap). Application has views with dashboards about Northwind customers: JavaScript dashboard (uses Infragistics jQuery widgets) MVC dashboard (uses Infragistics MVC library) Creating a dashboard using JavaScript The easiest way to add Infragistics jQuery controls is via Infragistics Script Loader. This is the reason to include this library in the bundle. Infragistics Loader is used via JavaScript in the same way like in other types of projects (see the code below) . The only one difference is in the path used when load other scripts - path starts from the site root – not from the current document location! 1: $.ig.loader({ 2: scriptPath: "/Scripts/Infragistics/js/", //local files version 3: cssPath: "/Content/Infragistics/css/", 4: resources: "igGrid.Selection.Paging.Sorting,igMap,igDataChart.*", 5: theme: "metro" 6: }); Using Infragistics jQuery controls. NetAdvantage for jQuery controls could be included via JavaScript in the same way like in other project types. 1: $("#map").igMap({ 2: width: "500px", 3: height: "500px", 4: panModifier: "control", 5: horizontalZoomable: true, 6: verticalZoomable: true, 7: windowResponse: "immediate", 8: overviewPlusDetailPaneVisibility: "visible", 9: seriesMouseLeftButtonUp: function (ui, args) { 10: var tets = args; 11: } 12: }); The code below shows the whole script, used for JavaScript Dashboard 1: <script type="text/javascript"> 2: jQuery.support.cors = true; 3: $.ig.loader({ 4: scriptPath: "/Scripts/Infragistics/js/", //local files version 5: cssPath: "/Content/Infragistics/css/", 6: resources: "igGrid.Selection.Paging.Sorting,igMap,igDataChart.*", 7: theme: "metro" 8: }); 9: $.ig.loader(function () { 10: var data = []; 11: var selected; 12: 13: $("#map").igMap({ 14: width: "500px", 15: height: "500px", 16: panModifier: "control", 17: horizontalZoomable: true, 18: verticalZoomable: true, 19: windowResponse: "immediate", 20: overviewPlusDetailPaneVisibility: "visible", 21: seriesMouseLeftButtonUp: function (ui, args) { 22: var tets = args; 23: } 24: }); 25: 26: 27: $("#chart").igDataChart({ 28: width: "650px", 29: height: "220px", 30: dataSource: "/Home/Orders", 31: axes: [{ name: "xAxis", type: "categoryX", label: "OrderID", labelVisibility: "visible" }, 32: { name: "yAxis", type: "numericY", labelVisibility: "visible"}], 33: series: [ 34: { name: "series", 35: title: "Order Freight Series", 36: type: "line", 37: xAxis: "xAxis", 38: yAxis: "yAxis", 39: valueMemberPath: "Freight", trendLineThickness: 6, thickness: 4, 40: trendLineBrush: "cyan", 41: transitionDuration: 1500, 42: trendLineType: "exponentialAverage" 43: }], 44: horizontalZoomable: true, 45: verticalZoomable: true, 46: windowResponse: "immediate", 47: overviewPlusDetailPaneVisibility: "visible" 48: }); 49: 50: $('#grid').igGrid({ 51: virtualization: false, height: 280, width: 650, 52: dataSource: "/Home/Customers", 53: autoGenerateColumns: false, 54: columns: [ 55: { headerText: "Customer ID", key: "CustomerID", width: "120px", dataType: "string" }, 56: { headerText: "Country", key: "Country", width: "150px", dataType: "string" }, 57: { headerText: "City", key: "City", dataType: "string" }, 58: { headerText: "Contact Name", key: "ContactName", dataType: "string" }, 59: {headerText: "Phone", key: "Phone", dataType: "string" } 60: ], 61: features: [ 62: 63: { 64: name: 'Selection', 65: mode: 'row', 66: multipleSelection: false, 67: rowSelectionChanged: function (ui, args) { 68: $("#chart").igDataChart({ 69: dataSource: "/Home/Orders?userID=" + args.row.element[0].cells[0].textContent 70: }); 71: 72: selected = args.row.element[0].cells[0].textContent; //keep track of selected user 73: var url = "http://nominatim.openstreetmap.org/search/" + args.row.element[0].cells[1].textContent + "?format=json"; 74: $.getJSON(url, 75: function (json, text) { 76: var name, lat, lon; 77: $.each(json, function (index, value) { 78: if (value.class === "place" && value.type === "country") { 79: name = value.display_name; 80: lat = parseFloat(value.lat); 81: lon = parseFloat(value.lon); 82: //alert(lat); 83: } 84: }); 85: data = [{ Name: name, Latitude: lat, Longitude: lon}]; 86: //add or override existing series named 'Countries' 87: //adding this series *after* the shapefile ones will cause the markers to appear above the shape lines, which is what we want 88: $("#map").igMap({ 89: series: [{ 90: name: "Countries", 91: type: "geographicSymbol", 92: longitudeMemberPath: "Longitude", 93: latitudeMemberPath: "Latitude", 94: /* 95: The provided object should have properties called render and optionally measure. 96: These are functions which will be called that will be called to handle the user specified custom rendering. 97: */ 98: markerTemplate: { 99: render: function (renderInfo) { 100: var ctx = renderInfo.context; //2d canvas context 101: var x = renderInfo.xPosition; 102: var y = renderInfo.yPosition; 103: 104: if (renderInfo.isHitTestRender) { 105: // This is called for tooltip hit test only 106: // Rough marker rectangle size calculation 107: ctx.fillStyle = "yellow"; 108: ctx.fillRect(x, y, renderInfo.availableWidth, renderInfo.availableHeight); 109: } else { 110: //actual marker drawing is here: 111: var markerData = renderInfo.data; 112: var name = markerData.item()["Name"]; 113: //set font or measure will be for the default one 114: ctx.font = '10pt Segoe UI'; 115: var textWidth = ctx.measureText(name).width; 116: 117: //Move the path point to the desired coordinates: 118: ctx.moveTo(x, y); 119: //Draw lines: 120: ctx.beginPath(); 121: ctx.lineTo(x - (textWidth / 2) - 5, y + 5); 122: ctx.lineTo(x - (textWidth / 2) - 5, y + 40); // 35width rect. 123: ctx.lineTo(x + (textWidth / 2) + 5, y + 40); // full textWidth line plus 5 margin 124: ctx.lineTo(x + (textWidth / 2) + 5, y + 5); // 35 up 125: ctx.lineTo(x, y); 126: //finish the shape 127: ctx.closePath(); 128: ctx.fillStyle = "rgba(78,183,226,0.7)"; 129: ctx.fill(); 130: ctx.lineWidth = 0.5; 131: ctx.strokeStyle = "#185170"; 132: ctx.stroke(); 133: //add a point at the start 134: ctx.beginPath(); 135: ctx.fillStyle = "black"; 136: ctx.arc(x, y, 1.5, 0, 2 * Math.PI, true); 137: ctx.fill(); 138: 139: // Draw text 140: ctx.textBaseline = "top"; 141: ctx.fillStyle = "black"; 142: ctx.textAlign = 'center'; 143: ctx.fillText(selected, x, y + 8); 144: ctx.fillText(name, x, y + 20); 145: } 146: } 147: }, 148: dataSource: data 149: }] 150: }); 151: //} 152: }); 153: 154: } 155: } 156: , 157: 158: { 159: name: 'Sorting', 160: type: "remote" 161: }, 162: { 163: name: 'Paging', 164: type: "local", 165: pageSize: 10 166: }] 167: , 168: rendered: function (ui, args) { 169: //set up on-load selection 170: $('#grid').igGridSelection("selectRow", 0); 171: //another way to get cell value independant of event parameters 172: var id = $('#grid').igGrid("getCellValue", 0, "CustomerID"); 173: $("#chart").igDataChart({ 174: dataSource: "/Home/Orders?userID=" + id 175: }); 176: 177: 178: } 179: }); 180: }); 181: </script> You could see below the HTML, which defines the view layout 1: <div style="display: block; height: 500px; width: 1155px;"> 2: <div style="float: left; width:650px; height: 280px; margin-right: 5px;"> 3: <table id="grid"> 4: </table> 5: </div> 6: <div style="float: right; top: -280px; height: 500px; width: 500px;"> 7: <div id="map" /> 8: </div> 9: <div style="position: relative; width: 650px; height: 260px; top: -220px; left: -650px; margin-right: 5px;"> 10: <div id="chart" /> 11: </div> 12: 13: </div> Application start page JavaScript dashboard in action! No worries! Everything works. Dashboard presents Northwind customers. When you select a specific customer from the grid you could see customer orders in the chart and his country (you will see a pushpin, that shows the county on the map). Creating a dashboard using MVC Wrappers The next step is to see how MVC libraries, built against MVC 3 work in ASP.Net MVC.4 projects. Infragistics.Web.Mvc library contains MVC wrappers of the Infragistics jQuery components. The whole logic is related with the application UI and you could use it without any issues in MVC 4 applications. MVC Dashboard view demonstrates how you could use MVC wrappers. The first step is to add Infragistics Script Loader with Razor. You could use the wrapper in the same way like in MVC 3 projects. The resources path should be started from the application root (like for the JavaScript dashboard). 1: @using Infragistics.Web.Mvc; 2: 3: @(Html.Infragistics().Loader() 4: .ScriptPath(Url.Content("/Scripts/Infragistics/js/")) 5: .CssPath(Url.Content("/Content/Infragistics/css/")) 6: .Theme("metro") 7: .Render() 8: ) The layout should be created like MVC 3 view layout with Razor – no changes! 1: <div style="display: block; height: 500px; width: 1155px;"> 2: <div style="float: left; width:650px; height: 280px; margin-right: 5px;"> 3: @(Html.Infragistics().Grid<jQueryMapMVC4Demo.Customer>() 4: .DataSourceUrl("/Home/Customers").ResponseDataKey("") 5: .ID("grid").Width("650px").Height("280px") 6: .LoadOnDemand(false) 7: .AutoGenerateColumns(false) 8: .Columns(column => 9: { 10: column.For(x => x.CustomerID).HeaderText("Customer ID").Width("120px").DataType("string"); 11: column.For(x => x.Country).HeaderText("Country").Width("150px").DataType("string"); 12: column.For(x => x.City).HeaderText("City").Width("120px").DataType("string"); 13: column.For(x => x.ContactName).HeaderText("Contact Name").DataType("string").Width("140px"); 14: column.For(x => x.Phone).HeaderText("Phone").DataType("string").Width("120px"); 15: }) 16: .Features(features => 17: { 18: features.Paging().Type(OpType.Local).VisiblePageCount(5).ShowPageSizeDropDown(true).PageSize(10).PrevPageLabelText("Previous").NextPageLabelText("Next"); 19: features.Sorting().Mode(SortingMode.Single).ColumnSettings(settings => 20: { 21: settings.ColumnSetting().ColumnKey("CustomerID").AllowSorting(true); 22: 23: }); 24: features.Selection().MouseDragSelect(true).MultipleSelection(false).Mode(SelectionMode.Row); 25: }) 26: .Width("650") 27: .DataBind() 28: .Render() 29: ) 30: </div> 31: <div style="float: right; top: -280px; height: 500px; width: 500px;"> 32: @(Html.Infragistics().Map() 33: .ID("map") 34: .Width("500px").Height("500px") 35: .VerticalZoomable(true) 36: .HorizontalZoomable(true) 37: .OverviewPlusDetailPaneVisibility(Visibility.Visible) 38: .BackgroundContent(bgr => bgr.OpenStreetMaps()) 39: .PanModifier(ModifierKeys.Control) 40: .WindowResponse(WindowResponse.Immediate) 41: //.WindowRect(0.27, 0.20, 0.5, 0.5) 42: .DataBind() 43: .Render() 44: ) 45: </div> 46: <div style="position: relative; width: 650px; height: 220px; top: 280px; margin-right: 5px;"> 47: @( Html.Infragistics().DataChart<jQueryMapMVC4Demo.Order>().DataSourceUrl("/Home/Orders").ResponseDataKey("") 48: .ID("chart") 49: .Width("650px") 50: .Height("220px") 51: .VerticalZoomable(true) 52: .HorizontalZoomable(true) 53: .Axes(axes => 54: { 55: axes.CategoryX("xAxis").Label(item => item.OrderID).LabelVisibility(Visibility.Visible); 56: axes.NumericY("yAxis"); 57: }) 58: .Series(series => 59: { 60: series 61: .Line("series").Title("Order Freight Series") 62: .XAxis("xAxis").YAxis("yAxis") 63: .ValueMemberPath(item => item.Freight).ValueMemberPath("Freight").TrendLineThickness(6).TrendLineBrush("blue") 64: .TrendLineType(TrendLineType.ExponentialAverage).TransitionDuration(1500) 65: .Thickness(4); 66: }) 67: .DataBind() 68: .Render() 69: ) 70: </div> 71: 72: </div> When you are using live events there also no changes. 1: <script type="text/javascript"> 2: var data = []; 3: var selected; 4: $('#grid').live('iggridselectionactiverowchanged', function (event, args) { 5: //set data chart source 6: $("#chart").igDataChart({ 7: dataSource: "/Home/Orders?userID=" + args.row.element[0].cells[0].textContent 8: }); 9: //set map series 10: selected = args.row.element[0].cells[0].textContent; //keep track of selected user 11: var url = "http://nominatim.openstreetmap.org/search/" + args.row.element[0].cells[1].textContent + "?format=json"; 12: 13: $.getJSON(url, 14: function (json, text) { 15: var name, lat, lon; 16: $.each(json, function (index, value) { 17: if (value.class === "place" && value.type === "country") { 18: name = value.display_name; 19: lat = parseFloat(value.lat); 20: lon = parseFloat(value.lon); 21: //alert(lat); 22: } 23: }); 24: data = [{ Name: name, Latitude: lat, Longitude: lon}]; 25: //add or override existing series named 'Countries' 26: //adding this series *after* the shapefile ones will cause the markers to appear above the shape lines, which is what we want 27: $("#map").igMap({ 28: series: [{ 29: name: "Countries", 30: type: "geographicSymbol", 31: longitudeMemberPath: "Longitude", 32: latitudeMemberPath: "Latitude", 33: /* 34: The provided object should have properties called render and optionally measure. 35: These are functions which will be called that will be called to handle the user specified custom rendering. 36: */ 37: markerTemplate: { 38: render: function (renderInfo) { 39: var ctx = renderInfo.context; //2d canvas context 40: var x = renderInfo.xPosition; 41: var y = renderInfo.yPosition; 42: 43: if (renderInfo.isHitTestRender) { 44: // This is called for tooltip hit test only 45: // Rough marker rectangle size calculation 46: ctx.fillStyle = "yellow"; 47: ctx.fillRect(x, y, renderInfo.availableWidth, renderInfo.availableHeight); 48: } else { 49: //actual marker drawing is here: 50: var markerData = renderInfo.data; 51: var name = markerData.item()["Name"]; 52: //set font or measure will be for the default one 53: ctx.font = '10pt Segoe UI'; 54: var textWidth = ctx.measureText(name).width; 55: 56: //Move the path point to the desired coordinates: 57: ctx.moveTo(x, y); 58: //Draw lines: 59: ctx.beginPath(); 60: ctx.lineTo(x - (textWidth / 2) - 5, y + 5); 61: ctx.lineTo(x - (textWidth / 2) - 5, y + 40); // 35width rect. 62: ctx.lineTo(x + (textWidth / 2) + 5, y + 40); // full textWidth line plus 5 margin 63: ctx.lineTo(x + (textWidth / 2) + 5, y + 5); // 35 up 64: ctx.lineTo(x, y); 65: //finish the shape 66: ctx.closePath(); 67: ctx.fillStyle = "rgba(78,183,226,0.7)"; 68: ctx.fill(); 69: ctx.lineWidth = 0.5; 70: ctx.strokeStyle = "#185170"; 71: ctx.stroke(); 72: //add a point at the start 73: ctx.beginPath(); 74: ctx.fillStyle = "black"; 75: ctx.arc(x, y, 1.5, 0, 2 * Math.PI, true); 76: ctx.fill(); 77: 78: // Draw text 79: ctx.textBaseline = "top"; 80: ctx.fillStyle = "black"; 81: ctx.textAlign = 'center'; 82: ctx.fillText(selected, x, y + 8); 83: ctx.fillText(name, x, y + 20); 84: } 85: } 86: }, 87: dataSource: data 88: }] 89: }); 90: 91: }); 92: 93: }); 94: 95: $('#grid').live('iggridrendered', function (event, args) { 96: $('#grid').igGridSelection("selectRow", 0); 97: //another way to get cell value independant of event parameters 98: var id = $('#grid').igGrid("getCellValue", 0, "CustomerID"); 99: $("#chart").igDataChart({ 100: dataSource: "/Home/Orders?userID=" + id 101: }); 102: }); 103: ript> Congratulations! You have a real MVC 4 dashboard with the Infragistics jQuery controls. If you want to move forward and start using ASP.Net MVC 4 you could use all your jQuery stuff and it’s MVC 3 wrappers. You can find ASP.Net MVC 4 Demo project here. Northwind sample database is available here. As always, you can follow us on Twitter: @mihailmateev and @Infragistics , all tweets with hashtag #infragistcs and stay in touch on Facebook, Google+ , LinkedIn and Infragistics Friends User Group !