Next Post(Continuation of this post): Simple CRUD with Knockout, Breeze, WEB API, Twitter bootstrap ,...
Hello,
As mentioned in my introductory post, English is not my mother tongue and so before I really start writing anything, I have to apologize in advance for any language mistakes I make.
Now let's start.
I'll try to go into details and do it as simply as I can, so even fresh new developers can understand what I am trying to say. The source code can be found at the github. So please take it, play with it, extend it...feel free to change it in any way and let me know what you think about it. The project is built using Microsoft Visual studio 2012 and SQL Server 2008R2, if you use Visual studio 2010 then you'll have to add yourself MVC4 templates.
So in short we have a simple UI with a grid (KoGrid) that is bound to ViewModel (Knockout observable collection). In the ViewModel we reference breezeJS data service which is querying data from our persisting service. That would all happen on the client side, while on the server side we set our persisting service which is ASP.NET Web API and by using Breeze.NET API and Repository pattern we request data from our model which is Entity Framework. The dependecies are managed by Autofac DI container. For the debugging purposes I have used a simple logger class.
The architecture that I am trying to build looks like the figure below.
In my example, everything will be a part of the same project even though they can and they should be sitting in the separate assemblies (e.g. WEB API, EF,...).
So lets start. Run your VS2012 - choose ASP.NET MVC 4 template - type the name of the project (TaxAppBlog). If you want to learn more about ASP.NET MVC 4 - David Hayden is writing a nice tutorial on his blog.
After you clicked ok, the new screen came up and there we choose the WEB API template.
After the visual studio finishes creating a project, the solution explorer will look like picture below.
Notice that there are two controllers created by default: Home and Values controller. The differences between them is that Home is inherited from System.Web.Mvc.Controller and Values from System.Web.Http.ApiController. If you are familiar with ASP.NET MVC then you already know how the Routing works. If I can explain it in one sentence then I would probably say that it is an interpretation of URLs by the server to decide what code should handle the request. The difference with ApiControllers is that the actions are not defined by URL but by HTTP methods so the same URL can contain two different HTTP methods, therefore they do two different actions (e.g. can retreive data(GET) or save data(PUT)). To learn more about routing and much more about ASP.NET MVC I would highly reccomend reading the book Pro ASP.NET MVC Framework by Steven Sanderson.
The Database First - begins with an existing database and reverse-engineer into a conceptual model.
The Model First - begins with an empty diagram - use the visual designer to design an EDM, then generate database schema from that model.
The Code First - begins with classes that describe your conceptual model.
In this example I'll use the Database First method. I will reverse-engineer only one table from my Tax database - Person table (again, I don't want here to build the whole Tax application but just to demonstrate how I would build a web app.) As mentioned above, the source code is on the GitHub and there you can find the SQL script that would generate and populate the database for you (in the script and in the source code on the GitHub I call the database TaxBlog, while here it is called Tax. It was changed after I wrote this post so I could continue working on my Tax app and have another database side by side). So if you want to follow and build the application while reading this blog, then make sure you build the database before you continue (see the Readme file on the GitHub).
Creating Model for Tax application (only Person)
And as a result we get the entity Person which represents the table from the Tax database.
To be honest we could use here DTOs (see the article that Antony Sneed wrote a long time ago) which might happen in some of the next blog posts but for now we'll be using classic enitites.
Hello,
As mentioned in my introductory post, English is not my mother tongue and so before I really start writing anything, I have to apologize in advance for any language mistakes I make.
Now let's start.
INTRODUCTION
So here today I'll build the application using the following technologies: ASP.NET MVC 4, Entity Framework 5.0, ASP.NET WEB API, AUTOFAC (DI container) and the following JavaScript frameworks: BreezeJS, JQuery, KnockoutJs, KoGrid,... Bear in mind that this is only for learning purposes! As I want to build a Tax calculation app, this will be a small excerpt of it, but showing the complete architecture (with only one screen : Tax payer screen). The purpose of this post is to show how easy (and clean and elegant) it is to get data from the server and bind them on the client using MVVM pattern and all the technologies I've listed above. Of course, along the way I'll try to convince you to use DI containers and couple of other techniques.I'll try to go into details and do it as simply as I can, so even fresh new developers can understand what I am trying to say. The source code can be found at the github. So please take it, play with it, extend it...feel free to change it in any way and let me know what you think about it. The project is built using Microsoft Visual studio 2012 and SQL Server 2008R2, if you use Visual studio 2010 then you'll have to add yourself MVC4 templates.
So in short we have a simple UI with a grid (KoGrid) that is bound to ViewModel (Knockout observable collection). In the ViewModel we reference breezeJS data service which is querying data from our persisting service. That would all happen on the client side, while on the server side we set our persisting service which is ASP.NET Web API and by using Breeze.NET API and Repository pattern we request data from our model which is Entity Framework. The dependecies are managed by Autofac DI container. For the debugging purposes I have used a simple logger class.
The architecture that I am trying to build looks like the figure below.
Tax payer application architecture |
SERVER
ASP.NET MVC 4
I chose the ASP.NET MVC. Why do I like the ASP.NET MVC? The list is long, but the first things that come to mind are: Separation of concerns, Testability, URL Routing, Built-in and shipped JQuery support :), WEB API (MVC-like framework that we can use to create a RESTful service), good mobile support, etc... The MVC lets you separate business logic from presentation logic so that they can be independently tested. It also lets you easily extend your application or replace any of the components as they are independent of each other.In my example, everything will be a part of the same project even though they can and they should be sitting in the separate assemblies (e.g. WEB API, EF,...).
So lets start. Run your VS2012 - choose ASP.NET MVC 4 template - type the name of the project (TaxAppBlog). If you want to learn more about ASP.NET MVC 4 - David Hayden is writing a nice tutorial on his blog.
After you clicked ok, the new screen came up and there we choose the WEB API template.
ASP.NET WEB API
It is, in my humble opinion, a huge step forward to make simple SOA architecture available to anyone. You remember the time when people started talking about SOA, it was a huge buzz word. You couldn't pass by any meeting room without hearing somebody mentioning it. Using services in your application was a powerful idea but making your code become available through services required learning different frameworks and doing a lot of plumbing and configuration to make it work. The learning curve was high, the apps that came out were brutal and the market was full of the proprietery technologies to make this happen, including WCF. With all this in mind Microsoft finally produced the framework that uses only open standards and is very easy to set it up and use. So here are a couple of bullet points on ASP.NET WEB API.- It is a framework for building pure HTTP based services, where the request and response happens with HTTP protocol.
- It is an ideal platform for building RESTful applications on the .NET Framework.
- Next iteration of WCF REST
- Incorporated into ASP.NET MVC 4
- Uses HTTP protocols
- Using REST to consume the service. Basic URL + HTTP method (GET, PUT, POST, DELETE).
After the visual studio finishes creating a project, the solution explorer will look like picture below.
Notice that there are two controllers created by default: Home and Values controller. The differences between them is that Home is inherited from System.Web.Mvc.Controller and Values from System.Web.Http.ApiController. If you are familiar with ASP.NET MVC then you already know how the Routing works. If I can explain it in one sentence then I would probably say that it is an interpretation of URLs by the server to decide what code should handle the request. The difference with ApiControllers is that the actions are not defined by URL but by HTTP methods so the same URL can contain two different HTTP methods, therefore they do two different actions (e.g. can retreive data(GET) or save data(PUT)). To learn more about routing and much more about ASP.NET MVC I would highly reccomend reading the book Pro ASP.NET MVC Framework by Steven Sanderson.
Entity Framework
To manage data in my application I'll use entity framework 5.0. EF provides you with three ways to define the model of your entities:The Database First - begins with an existing database and reverse-engineer into a conceptual model.
The Model First - begins with an empty diagram - use the visual designer to design an EDM, then generate database schema from that model.
The Code First - begins with classes that describe your conceptual model.
In this example I'll use the Database First method. I will reverse-engineer only one table from my Tax database - Person table (again, I don't want here to build the whole Tax application but just to demonstrate how I would build a web app.) As mentioned above, the source code is on the GitHub and there you can find the SQL script that would generate and populate the database for you (in the script and in the source code on the GitHub I call the database TaxBlog, while here it is called Tax. It was changed after I wrote this post so I could continue working on my Tax app and have another database side by side). So if you want to follow and build the application while reading this blog, then make sure you build the database before you continue (see the Readme file on the GitHub).
Creating Model for Tax application (only Person)
- Select Add and then New Item from the menu.
- Select ADO.NET Entity Data Model from the filtered list of item templates. Change the name of the model to Tax.edmx and click Add
- In the Choose Model Contents window, select Generate from database and then click the Next button.
- On the Choose Your Data Connection page, select Tax database in the data connection drop down and click Next.
- Then save entity connection settings in the Web.config and click Next.
- Then choose only table Person from the database.
To be honest we could use here DTOs (see the article that Antony Sneed wrote a long time ago) which might happen in some of the next blog posts but for now we'll be using classic enitites.
Breeze.NET API
Breeze is made by the company called IdeaBlade and it is basically, a JavaScript data management library. It helps you manage data in rich client application. Breeze has a two parts of the setup: server and client. First we'll talk about the server part. There are a lot of reasons why I really like and choose Breeze but one that helps me decide is that Breeze ships with out-of-the-box support for the ASP.NET Web API and Entity Framework. As my example uses ASP.NET Web API controller (which will be covered further in the post) to handle the HTTP requests from the client and it uses the Entity Framework to model and access a SQL database, the Breeze offers me a wrapper component (called EFContextProvider) around the application's DbContext. It takes care of a lot of routine plumbing. I'll use out-of-the-box EFContextProvider but in real life you would probably customize it (e.g. intercept save requests and validate them, etc...). As we really appreciate a separation of concerns and loosely coupled layers: we always try to reference interfaces so that the concrete implementation can be supplied at runtime. In that light, I will build repository pattern which allows me to easily replace EF with some other ORM (e.g. NHibernate,etc...) But before that I'll add Breeze for ASP.NET Web API using NuGet.
Now I'll build the repository interface:
Now I'll build the repository interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TaxAppBlog.Models.Contracts
{
public interface IBreezePersonRepository
{
IQueryable People { get; }
string Metadata();
}
}
Using IQueryable I am taking advantage of oData. Now I can use OData syntax of paging and querying people which I'll present you later in the post. Metadata is needed for the Breeze setup. "Breeze needs this metadata to communicate with the persistence service during query and save, to create new entities on the client, and to navigate among entities in cache. Because Breeze has metadata, it can generate your JavaScript model “classes” on the fly."
Now we'll be adding a new class BreezePersonRepository which will implement the interface IBreezePersonRepository.
Here we'll be using a Breeze EFContextProvider to query on EF or to get breeze metadata.
And finally BreezePersonRepository looks like this:
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Breeze.WebApi;
using TaxAppBlog.Models.Contracts;
namespace TaxAppBlog.Models.Implementations
{
public class BreezePersonRepository : IBreezePersonRepository
{
readonly EFContextProvider _contextProvider =
new EFContextProvider();
public IQueryable People
{
get { return _contextProvider.Context.People; }
}
public string Metadata()
{
return _contextProvider.Metadata();
}
}
}
Now when the Breeze is set on the server, we are ready to build our WEB API. But before we do that we'll set the Autofac as our DI container.
Autofac - IoC container
Even though in my small example I wouldn't need to use any of IoC containers I did it to show that it's not so complicated (there is a lot of fear around about the complexity of the setup of the Ioc containers). I strongly advise everybody to use it when they build their application. At my work we are mostly using Unity but here I'll set another one which is called Autofac. I chose Autofac as it had correct benchmark results (benchmarks from some time ago IoC Container Benchmark - Performance comparison; IoC Container Benchmark ReRevisted - Ninject updated, Autofac added ) and it has ASP.NET MVC 4 Integration package as well as ASP.NET WEB Api Integration package. So to start adding the Autofac we'll call in help...who else but NuGet :) We'll be adding Autofac and its two integrations (ASP.NET MVC 4 and WEB Api)
To configure Autofac we'll add a new class and give it a name AutofacConfig:
In this class we'll register all MVC controllers then API controllers. We'll also set their lifetime scope: InstancePerApiRequest is part of the Web API integration, and InstancePerHttpRequest is part of the MVC integration. They both apply the same tag to the lifetime scope but it was done this way because you might have services that are dependencies of both Web API and MVC controllers. Then we register our container for MVC controllers and API controllers.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using Autofac.Integration.WebApi;
using TaxAppBlog.Models.Implementations;
namespace TaxAppBlog.App_Start
{
public class AutofacConfig
{
public static void Register(HttpConfiguration config)
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(WebApiApplication).Assembly);
builder.RegisterApiControllers(typeof(WebApiApplication).Assembly);
builder.RegisterType().AsImplementedInterfaces().InstancePerApiRequest().InstancePerHttpRequest();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
}
Now we need to call this class at the start of the application so we call it from Global.asax
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using TaxAppBlog.App_Start;
namespace TaxAppBlog
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
AutofacConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}
So kids, that wasn't difficult :). Start using DI containers in your future projects if you haven't already done so. Now for the last element of server side setup we'll build our WEB Api service.
WEB Api
We'll add a new ApiController called BreezePeopleController (probably unfortunate name - should have omitted Breeze from the name).
Now our controller will look very simple
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Breeze.WebApi;
using TaxAppBlog.Models;
using TaxAppBlog.Models.Contracts;
namespace TaxAppBlog.Controllers
{
[BreezeController]
public class BreezePeopleController : ApiController
{
IBreezePersonRepository repository;
public BreezePeopleController(IBreezePersonRepository repository)
{
this.repository = repository;
}
public IQueryable GetPeople()
{
return repository.People;
}
// ~/api/BreezePeople/Metadata
[HttpGet]
public string Metadata()
{
return repository.Metadata();
}
}
}
You noticed that our controller is decorated with the attribute BreezeController. Now why did we do that? "A Breeze server (read Web API controller) and a Breeze client (read BreezeJS) have a shared understanding about the nature and format of HTTP requests, responses and payloads. The Web API pipeline has to be configured to conform to that "understanding". Because your Breeze Web API Controller may cohabitate with other, non-Breeze controllers, Breeze shouldn't impose its configuration requirements on them. Therefore, we should configure the Web API pipeline on a controller basis ... and that's what this BreezeController attribute does.
When the Web API routes a request to the BreezePeopleController, it creates a new instance of that controller type and then calls the BreezeController attribute to configure the Web API pipeline for this controller instance only.
Always decorate your Breeze controller with the BreezeController attribute."
We used Constructor Injection to inject references of our IBreezePersonRepository implementation (here one can see how this is really decoupled.)
Before we go further and setup the client we'll test the server side setup. In short, we'll run the application and request the URL of the method GetPeople.
As a result we'll get the JSON formatted results. The question to open it pops up...
...and after we open it in notepad we see our data in JSON format.
Here I am going to show how you can inspect the results in more elegant way by using Fiddler. First you enter the url in the composer and execute it.
Then go to the Inspector tab and verify your results.
As the last thing before we move on to the client setup, I'll show you how to query/sort data using OData syntax (which I promised above while setting People property as IQueryable). Again, open the Composer tab in the Fiddler and change the URL into something like this: http://localhost:17612/api/breezePeople/getPeople?$top=1&$orderby=FamilyName (where 17612 is only my asp.net development web server port - if you run your project in visual studio chances are you are going to get a different port number). Take note that we specified that we want the top 1 record and to be ordered by FamilyName.
After executing composer, go and verify your results (Inspector tab) - we actually retreived only one record and sorted by FamilyName.
Finally, the server side is up and running so we can move to the client side.
CLIENT
BreezeJS
For client side setup we'll start with BreezeJS. In general, what we'll do is download script libraries by using NuGet then we'll "bundle" them and we'll also add reference in the _Layout.cshtml so they can be available in all views (only those that are using _Layouts.cshtml as master page of course). Breeze scripts are already downloaded and added to the project with the package "Breeze for ASP.NET Web Api projects" that we got during the Breeze setup in the server section (see above "Breeze.NET API").
On the client side we work with Breeze Entity manager class. The Entity Manager is a core class in Breeze. When the client app asks for data it calls a method on an instance of an Entity Manager. Entity Manager is a gateway to our persistance model. It reads the data model supplied by our services metadata and produces a Java Script "entity" object which we'll use further for our data binding. The brilliant thing is that it doesn't require you to do anything on the client side in terms of manually defining entity classes or write any mapper. The Entity Manager acts as its own data context on the client side which means when you create multiple instances of the Entity Manager then they are completely independent, each having its own cache. What they share is that they talk to the same service and they share the same Metadata store. I prefer to have a shared entity manager. The way that we can achieve this is if we encapsulate the application manager within the "dataservice" module. All the other modules reference this dataservice module and ask it for data.
(function (root) {
var breeze = root.breeze;
var app = root.app = root.app || {};
// show logger initially
Logger.show();
log("Window is loaded.");
var serviceName = 'api/BreezePeople';
var manager = new breeze.EntityManager(serviceName);
// add members to the dataservice
var dataservice = {
getAllPersons: getAllPersons
};
// extend the app with this dataservice
app.dataservice = dataservice;
// gets all Persons asynchronously
// returning a promise you can wait for
function getAllPersons(peopleArray) {
log("querying for all persons")
var query = new breeze.EntityQuery()
.from("GetPeople");
return manager
.executeQuery(query)
.then(function (data) {
processResults(data, peopleArray);
})
.fail(queryFailed);
}
// clears observable array and loads the person results
function processResults(data, peopleArray) {
log("Clears observable array and loads the person results.");
peopleArray.removeAll();
var persons = data.results;
persons.forEach(function (person) {
log("adding " + person.FirstName._latestValue + " " + person.FamilyName._latestValue);
peopleArray.push(person);
});
}
function queryFailed(error) {
log("Query failed: " + error.message);
}
}(window));
For debugging purposes I've used a logger class which prints debugging messages on a separate console window at the bottom of the web browser (which is very handy). The logger can be found here. So what we did here is create an entity manager and use it to execute the query (.executeQuery(query)) which is an asynchronous method and returns a promise to call back either the processResults method if the query succeeds or the queryFailed method if the query execution fails with an exception. The dataservice is added to the namespace app so it can be called from different viewmodels. Now we finally come to UI data binding with Knockout.
KnockoutJS
First we'll download Knockout using NuGet:
...then we'll "bundle" them:
As a big fan of the KnockoutJS I'll say just a couple of words about what KO (short for Knockout) is, what it is not and why and how to use it. KnockoutJS is a JavaScript library that helps you to create rich and responsive interfaces with a clean underlaying data model by using a MVVM approach. Basically, when you make calls via ajax and update your UI elements, things can get a little messy. Knockout can make it simpler and more uniform. It is a self contained JavaScript library (14Kb - gzip), supports all mainstrem browsers and what is really nice - no dependencies. It is important to understand that it is not a replacement for JQuery and it is not a Prototype for JavaScript. There are three core features of KnockoutJS. The first is Observables and dependency tracking - it updates your UI automatically. When you change a viewmodel it updates UI elements also. How can Knockout know that a part of your viewmodel changed, well the answer is: it needed to query model properites as observables because they are special JavaScript objects that can notify the subscribers about changes and then it can automatically detect dependencies. The second feature is Declarative Bindings - this is a simpe and obvious way to connect a part of your UI to your data model. You can now construct complex dynamic UI easily and arbitrarily using these databinding contracts. The third important feature is Templating - Template binding populates the DOM element with the results of rendering template. This means that teamplates are a simple and convinient way to build UI structures possibly with repeating or nested blocks as a function of your viewmodel data. To learn more on KnockoutJS please go here. One of the reasons that Knockout was created was enabling MVVM style development for websites/web applications. So I'll say a couple of words about it also. MVVM is a design pattern for building user interfaces. It describes how you can keep a potentially sophisticated UI simple by splitting it in three parts (separation of concerns). The first part is Model. The model is simply your application store data (in our application we use breeze/web api to read and write model data) The second part is ViewModel - Entity(field) structure of your data. This is a pure code representation of the data and operations in the UI. When using KnockoutJS your ViewModel is pure JavaScript that holds no knowledge of HTML. Keeping the ViewModel abstract this way let us stay simple so we can easily manage more sophisticated behaviour without getting lost :) And finally the last part is View - UI representation of the current state of ViewModel and interactions within it. So when using KnockoutJS your view is simply in your HTML document with declarative binding that links to the ViewModel (alternatively you can use templates that generate HTML using data from your ViewModel). The beauty of solid MVVM library such as Knockout is that you can focus on develping business logic instead of writing code to attach/detach event handler an manually update UI elements whenever data values change. Our ViewModel exposes people as observableArray and hide as observable bool to be bound on the view.
(function (root) {
var app = root.app;
var dataservice = app.dataservice;
var vm = {
people: ko.observableArray([]),
hide: ko.observable(true)
};
getAllPersons()
// reveal view when query succeeds
.then(function () { vm.hide(false); });
app.peopleViewModel = vm;
function getAllPersons() {
return dataservice.getAllPersons(vm.people);
}
}(window));
To get data we used our breeze dataservice. We also added our viewModel (peopleViewModel) to the app namespace so it can be accessed and data bound in the Main.js
(function (root) {
var app = root.app;
log("Breeze Devices is booting");
ko.applyBindings(app.peopleViewModel);
}(window));
KoGrid
And finally in the view I will use KoGrid.
I haven't explored KoGrid much, but as far as I have played with it, it is extremely simple and friendly to make work with KnockoutJS. As for all other libraries we'll first download it and add it to the project using NuGet.
Now when all libraries are downloaded and added to the project I will show you what Bundle.Config looks like:
using System.Web;
using System.Web.Optimization;
namespace TaxAppBlog
{
public class BundleConfig
{
// For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
"~/Scripts/knockout-{version}.js",
"~/Scripts/knockout-{version}.debug.js"));
bundles.Add(new ScriptBundle("~/bundles/koGrid").Include(
"~/Scripts/koGrid.min.js",
"~/Scripts/koGrid.debug.js"));
bundles.Add(new ScriptBundle("~/bundles/q").Include(
"~/Scripts/q.js",
"~/Scripts/q.min.js"));
bundles.Add(new ScriptBundle("~/bundles/breeze").Include(
"~/Scripts/breeze.js",
"~/Scripts/breeze.debug.js",
"~/Scriptsbreeze.intellisense.js"));
bundles.Add(new ScriptBundle("~/bundles/logger").Include(
"~/Scripts/App/Logger.js"));
bundles.Add(new ScriptBundle("~/bundles/app").Include(
"~/Scripts/App/TaxDataService.js",
"~/Scripts/App/TaxPayerViewModel.js",
"~/Scripts/App/main.js"));
// Use the development version of Modernizr to develop with and learn from. Then, when you're
// ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/site.css",
"~/Content/KoGrid.css",
"~/Content/ie.css",
"~/Content/print.css",
"~/Content/screen.css"
));
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/jquery.ui.core.css",
"~/Content/themes/base/jquery.ui.resizable.css",
"~/Content/themes/base/jquery.ui.selectable.css",
"~/Content/themes/base/jquery.ui.accordion.css",
"~/Content/themes/base/jquery.ui.autocomplete.css",
"~/Content/themes/base/jquery.ui.button.css",
"~/Content/themes/base/jquery.ui.dialog.css",
"~/Content/themes/base/jquery.ui.slider.css",
"~/Content/themes/base/jquery.ui.tabs.css",
"~/Content/themes/base/jquery.ui.datepicker.css",
"~/Content/themes/base/jquery.ui.progressbar.css",
"~/Content/themes/base/jquery.ui.theme.css"));
}
}
}
...and also what our master page looks like containg all necessary references:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/logger")
</head>
<body>
<nav class="container">
<div id="header" >
<div id="logo">
<h1>Tax Application (sample for blogging purposes :))</h1>
</div>
</div>
<nav>
<ul id="menu">
<li>@Html.ActionLink("Home", "Index", "TaxPayer")</li>
<li>@Html.ActionLink("Tax Payer", "Index", "TaxPayer")</li>
</ul>
</nav>
<div id="main">
@RenderBody()
</div>
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/knockout")
@Scripts.Render("~/bundles/koGrid")
@Scripts.Render("~/bundles/q")
@Scripts.Render("~/bundles/breeze")
@Scripts.Render("~/bundles/app")
@RenderSection("scripts", required: false")
</body>
</html>
Notice that we are referencing JavaScript files in the bottom of the page. Why? Well first because the entire visual markup is processed before the script references are and thus will be displayed on the screen while the referenced JavaScript libraries are downloading and being processed into browser memory; and second some of this libraries works with DOM (e.g. Knockout) and in order to do so the browser needs to have created and rendered portions of the DOM before the Knockout executes.
Now finally in the TaxPayer view we bind our grid to the "people" propery of our viewModel but also we use the property "hide" to first show loading gif(ajax-loader.gif) while data is loading and then hide the gif but show the grid:
Now finally in the TaxPayer view we bind our grid to the "people" propery of our viewModel but also we use the property "hide" to first show loading gif(ajax-loader.gif) while data is loading and then hide the gif but show the grid:
@{
ViewBag.Title = "Index";
}
<!DOCTYPE html>
<html>
<body>
<div id="body">
<div id="imageWrapper" align="center">
<img id="loader" src="images/ajax-loader.gif" data-bind="visible: hide()" />
</div>
<div id="gridWrapper" data-bind="visible: !hide()">
<div id="sandBox" style="height: 200px;" data-bind="koGrid: { data: people,
columnDefs: [{ field: 'FirstName', width: 100 },
{ field: 'FamilyName', width: 110 },
{ field: 'BirthDate', width: 100 },
{ field: 'Profession', width: 100 },
{ field: 'Telephone', width: 100 },
{ field: 'Address', width: 150 },
{ field: 'City', width: 100 },
{ field: 'Country', width: 100 },
{ field: 'Email', width: 150},
{ field: 'Twitter', width: 110 }
],
autogenerateColumns: false,
isMultiSelect: true,
enablePaging: false }">
</div>
</div>
</div>
</body>
</html>
Here you can see how easy and elegantly we bind our kogrid. Now when we run the project we'll first get the loading gif:
So here we have a taxpayer screen up and running. Now we can think of adding more features (e.g. add/update/delete) and e.g. setup all maintenace screens uniformly like this one.
Thanks for reading this post and letting me know if you like it or not, what would you change and how.
What is the reasoning behind the stictly defined Bundling Configuration? Can't you just have Content/js and Content/css and have anything in those folders bundled into one? It of course raises the need to prefix the files with 010_ 020_ etc. giving with 10 increments the space to reorder the bundling for later, but in my experience it is very easy to communicate such thing to UI Programmers. In a "normal" project, I would not do this, but since this post is about MVVM which in core will expect more aware UI Programmers, the expectation is, that they understand this stuff which gives a reason to provide them some flexibility. For example, they might not like your pick on the jQuery.Ui and they might know better to switch it into something else.
ReplyDeleteFirst of all sorry for late reply and thanks a lot for your comment. To answer your question as someone who likes very much ASP.NET MVC4 Bundling and minification - I really don't know why would you use some custom methods of bundling when everything is already built for you (Optimization, Caching,...etc). In my post I choose ASP.NET MVC 4 and I think I'd be silly not to use bundling and minification as it comes out of the box. In the App_Start\BundleConfig.cs file there is a method RegisterBundles which is used to create, register and configure bundles.You can add .debug and .min versions of the files which will be used based on the the value of the debug attribute in the compilation Element in the Web.config file (). You can override the Web.config setting with the EnableOptimizations property on the BundleTable class (inside of the bundle configuration). There is also a bundle cache configured for you...all this and much more you can explore here ->(http://www.asp.net/mvc/tutorials/mvc-4/bundling-and-minification). I hope this answer your question. Regards, Tomislav
DeleteHi,
ReplyDeleteYou article is great, congratulation.
It is very detailed, and describes necessary steps very clearly.
I would like to ask when could you write another one for CRUD functionality with this technology - add / edit / delete with BreezeJS and WebAPI?
Thanks
Hello Bogdan,
DeleteThank you so much on your comment and nice words. These days I am a bit busy (for the company needs I am doing some sharepoint certification and also I am preparing the exam for the Luxembourgish language plus my regular work plus private activities (3 small kids :) )). As soon as I find some time (read: in next a couple of days) I'll write a small example with CRUD functionality using the libraries from my post.
Regards,
Tomislav
Hello Bogdan,
DeleteFinally I found some time to post simple CRUD using Knockout/Breeze/WebApi/... Thanks for your patience :) Regards,
Tomislav
Nice post very helpful
ReplyDeletedbakings
Thanks Sudhir, it's always nice to hear that :)
DeleteReally good article, learnt a lot, one thing that always frustrates me however with most tutorials on the web is that they very rarely use foreign key tables, in my experience this is where the difficult bits come in.
ReplyDeleteFor example if instead [Person] having [Email] & [Twitter] fields it had a navigation property to [ContactMethods] which linked to [ContactMethod] which in turn linked to [ContactMethodType] (Twiter, Email)
Anyway if you want a idea for another tutorial :-)
Good article.
Phil
Hello Phil,
DeleteGlad you've liked it. I'll consider to put some more realistic scenario in my next post (e.g. like you suggested: using a different referential integrities on the tables, etc...). Thanks again :)
Hello Tomislav,extremely good article, thank you.
ReplyDeleteFilter, that you are using in koGrid, looks completely different from filters at koGrid example page.
http://knockout-contrib.github.io/KoGrid/#/examples
And yours looks much prettier, how do you do this?
Hello xxx,
DeleteThanks for your comment :) My databinding on KoGrid is very similar to the one from the KoGrid examples. The difference is that I've got data from database and have it in view model while they build mock data and bind it in the same place. Then I used the column definitions (the same way as in their example "Column Definitions Example") in order to show the grid columns the way I wanted. If you're not happy with this let me know (I can elaborate further). KoGrid is very nice and elegant way to show data using java script but the only trouble I've had with it is to find out which version KoGrid, Knockout and Jquery work together :( Sorry for late response, it's holiday season :) Thanks again.
Have you used kogrid with wcf services? I need server side paging. Do we need to create pagesize parameter in wcf service?
ReplyDeleteKiran,
DeleteWhen you say wcf service you probably thought of the previous version of wcf service (before WEB API) with one of the HTTP bindings (BasicHttpBinding, WSHttpBinding) so my answer is no I haven't use KoGrid with it. Moreover I decided not to use KOGrid in my production code so I wouldn't be able to help you unless I try your example myself. I would imagine that you could create pagesize parameter (e.g. you would use to get portion of data for your grid) and use it when you set your KoGrid in the ViewModel. You can find very simple example (where pagesize is hardcoded) in the Ryan Niemeyer fiddle -> http://jsfiddle.net/rniemeyer/qsrbr/ . There you can replace getting data from your service and also the pagesize can be variable and be sent from your service. Sorry I couldn't help you more but if you really want you can send me your code and I can see what I can do. Thanks.
Hi, Nice description and nice codes about how to Build application using ASP.NET MVC.Thanks, its really helped me a lot
ReplyDelete-Aparna
Theosoft
Thanks John for nice words. I'm glad it helped you :)
DeleteNice Article.For if any want good look and feel for KoGrid check this link:
ReplyDeletehttp://articlesforprogramming.blogspot.in/2013/09/knockout-kogrid-in-aspnet-mvc4.html
Asp.Net is best and popular programming language in the world. Thanks to share amazing information about .NET Application Development.
ReplyDeleteThis was not working until I moved the document ready function to the top of the page. No idea why. If I get time I'll investigate.
ReplyDeleteOne other thing, where's the edit functionality? Display grids are the easy path. I still have not found an example of a Knockout grid with edit functionality. Every man and his dog has a tutorial on display grids. I find the kogrid to be a bit featureless.
ReplyDeleteFirst of all, thank you for a great article. One of the many things that troubles me is the metadata from the database first. I know you get a bunch of entities back when you generated the model, but what if you have a custom model based on the out of the box person entity. For instance, i want to add a person full name as a property in the custom model and have metadata full name is of type string with minlength, and it is required. How do breeze or how do i tell breeze to get the custom model metadata?
ReplyDeleteperde modelleri
ReplyDeletesms onay
mobil ödeme bozdurma
nft nasıl alınır
ankara evden eve nakliyat
trafik sigortası
dedektör
WEB SİTESİ KURMA
Ask kitaplari
özel ambulans
ReplyDeletenft nasıl alınır
uc satın al
en son çıkan perde modelleri
yurtdışı kargo
minecraft premium
lisans satın al
en son çıkan perde modelleri