With the web getting more and more interactive JavaScript frameworks is becoming a must in every web project (unless you want to code it yourself). There are a lot to choose from like Ember, Knockout, Backbone, Agility, Kendo UI, etc and it’s all up to you whats the most compatible framework depending on your requirement, there is no right or wrong framework, its all a matter of preference like us we prefer Angular JS and Kendo UI most of the time.
That means I am not here to discuss what the best framework is but I am here to show how to use Angular JS with our current MVC application and it was surprisingly easy.
Before we start this one make sure that you are already using WebApi (ApiControllers) if not read this article first as it explains why.
Now lets start by adding Angular Core to your project using NuGet
search for Angular then install
Once installed add the Angular minifined JS to your Bundle if you are using BundleCollection.
or if not just add the script reference just before you close the head section of your HTML. Now you are ready to use it. Lets now create your ApiControllers that will be used by Angular.
Lets say you have a simple table with two fields which is called Equipment, this table will contain a simple information whether an Equipment is available or not. This means we only have four columns/fields which is the EquipmentId, Name, Quantity and IsDiscontinued, this is now how your view model would look like.
public class EquipmentsViewModel { [DisplayName("Equipment Id")] public int? EquipmentId { get; set; } [DisplayName("Name")] [StringLength(255)] public string Name { get; set; } [DisplayName("Quantity")] public int Quantity { get; set; } [DisplayName("Is Discontinued")] public bool IsDiscontinued { get; set; } }
Now lets create Get, Post and Delete Methods on your ApiController which will be called by the Angular Controller that you will be building. You will notice we already have Queries and Tasks in place which takes care of the Data Gathering and the Business Processes at the back-end. You need to make sure it will be as generic as this as possible so when you create your Angular Controller it will be easy.
public class EquipmentsApiController : ApiController { private readonly IEquipmentsQuery equipmentsQuery; private readonly ICommandProcessor commandProcessor; public EquipmentsApiController( IEquipmentsQuery equipmentsQuery, ICommandProcessor commandProcessor) { this.equipmentsQuery = equipmentsQuery; this.commandProcessor = commandProcessor; } public IEnumerable<EquipmentsViewModel> Get() { var result = equipmentsQuery.GetAll(); return result; } public EquipmentsViewModel Get(int id) { var result = equipmentsQuery.Get(id); return result; } [Transaction] public void Post(EquipmentsViewModel viewModel) { if (ModelState.IsValid) { var command = new SaveOrUpdateEquipmentCommand( viewModel.EquipmentId, viewModel.Name, viewModel.Quantity, viewModel.IsDiscontinued ); if (ModelState.IsValid) { commandProcessor.Process(command); viewModel.EquipmentId = command.EquipmentId; } } } public void Delete(int id) { var command = new DeleteEquipmentCommand(id); commandProcessor.Process(command); } }
I hope this looks straightforward, the get executes queries while the post and delete executes Business Processes you defined. Don’t forget you also need the normal MVC controllers to show your Form, in this example we create one called CreateOrUpdate and it will cater for inserting or updating data. You will notice if there is an id parameter that is passed it adds to the view models so we know on the client side which is a create event or update.
public class EquipmentsController : Controller { public ActionResult CreateOrUpdate(int? id) { var viewModel = new EquipmentsViewModel { EquipmentId = id }; return View("Form", viewModel); } }
Now lets build your Angular controller that will work with the ApiController above and any ApiController following the same naming convention as above. You can give this any file name you want just don’t forget to include this on your Bundle.
function () { var yourWebApp = angular.module('yourWebApp', []); yourWebApp.controller('getRequest', function($http, $scope) { $scope.loading = true; $scope.addMode = false; $scope.editMode = false; $scope.returnMessage = ""; $scope.thisControllerName = ""; $scope.data = []; $scope.getType = function(x) { return typeof x; }; $scope.init = function(controllerName, id) { $scope.thisControllerName = controllerName; if (id != "") { $scope.id = id; $scope.controllerName = controllerName; $http.get('/api/' + $scope.controllerName + 'Api/' + $scope.id).success(function(data) { $scope.data = data; $scope.loading = false; $scope.editMode = true; }) .error(function() { $scope.error = "An Error has occured getting list"; $scope.loading = false; }); } else { $scope.addMode = true; } }; $scope.reset = function() { $scope.data = []; }; $scope.save = function(isValid) { if (isValid) { $http.post('/api/' + $scope.thisControllerName + 'Api/', $scope.data).success(function(data) { //show success message $scope.returnMessage = "Your request has been posted successfully"; //clear form. if (!scope.editMode) { $scope.data = []; $scope.modelForm.$setPristine(); } $scope.loading = false; }) .error(function() { $scope.returnMessage = "An Error has occured getting list"; $scope.loading = false; }); } }; }); }})();
Finally lets build the forms and based on the MVC controller it should be called Form.cshtml (and don’t mind the bootstrap classes, I am not bothered removing it for this demo)
@model YourWebApp.Web.Mvc.Controllers.ViewModels.Equipments.EquipmentsViewModel @{ ViewBag.Title = "Equipments"; Layout = "~/Views/Shared/_Layout.cshtml"; } <form name="modelForm" ng-controller="getRequest as data" ng-init="init('Equipments','@(Model.EquipmentId)')" ng-submit="save(modelForm.$valid)" novalidate class="container-fluid" > <h2>Equipments</h2> <div class="row"> <div class="form-group col-sm-6"> @Html.LabelFor(m => m.Name) <input type="text" ng-model="data.Name" class="form-control" required ng-maxlength="255"> </div> <div class="form-group col-sm-6"> @Html.LabelFor(m => m.Quantity) <input type="number" ng-model="data.Quantity" class="form-control" required> </div> <div class="form-group col-sm-6 wrap-row"> @Html.LabelFor(m => m.IsDiscontinued) <div class="row"> <div class="col-xs-1"> <input type="checkbox" ng-model="data.IsDiscontinued" /> </div> <div class="col-xs-11">Is Discontinued?</div> </div> </div> </div> <div class="button-container"> <button class="btn btn-primary" type="submit" ng-show="editMode">Save</button> <button class="btn btn-primary" type="submit" ng-show="addMode">Create</button> <button class="btn btn-link" type="reset" ng-click="reset()" ng-show="addMode">Reset</button> {{returnMessage}} </div> </form>
Now lets draw some visual lines on how each elements relate to each other and this is where the Angular JS magic happens. First lets see how the Form Relates to the Angular Controller.
I think I don’t need to further explain, those lines should be clear enough how they map together.
And this is how the Angular Controller relates to the ApiController.
Now all you need is to configure and register this additional routes which you can do in your Global.asax, this route will be then used by AngularController route to the correct ApiController.
configuration.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); configuration.Routes.MapHttpRoute( "DefaultApi", "Api/{controller}/{id}", new { id = RouteParameter.Optional } );
Finally you need to add the following attribute on your html tag, usually its in your _Layout.html file
<html ng-app="yourWebApp">
You’re all finished now you can try it, so if you want to create a new entry based on the MVC controller above you need to go to:
http://localhost:9999/Equipments/CreateOrUpdate
or if you want to update and Entry you can do something like this
http://localhost:9999/Equipments/CreateOrUpdate/1
Easy isn’t it?
Nice…!!!
Excellent ! … I hope it works, thanks.