Monday, March 23, 2015

How to Log MVC troubleshooting and error messages to the Events Viewer

In this post we describe Step by step How to Log MVC troubleshooting and error messages to the Events Viewer.  
We implement here in just 5 minutes an error events log , monitoring and troubleshooting messages  from your MVC application to the windows Events Viewer. This way, all errors will be preserved to the windows events log.
Also we set a custom error page for your MVC app, so that the users can see an appropriate error page:




Step by step How to Log MVC troubleshooting and error messages to the Events Viewer



First, let's type some error in the code, something like dividing by zero:




Rebuild and run your app, and browse to the MVC View corresponding to the Controller you chose:
 

OK, we have the Exception and an YSOD typical page ("Yellow Screen Of Death"). 
First, we cannot allow users to see this screen, so let's change the settings at the web.config file:


Inside the <system.web> element , add a <customErrors> and set the "mode" to "On" or "RemoteOnly", saying that a custom error page will be shown to remote users or to everyone.
Browse again to see the error:

OK, now the users don't see the YSOD page.
Now, let's log the errors to the Windows Event Viewer, by adding an Application_Error event handler to the Global.asax file:




Here we instantiate an EventLog object with the following information:
1) the URL where the exception was thrown: we get this from the Request.Path variable
2) the Exception Message and info: this we get from the Server.GetLastError() method
3) we set the "Source" of the error as "Application"
4) the Error Type will be EventLogEntryType.Error

Save and build the project. Open the Event Viewer:



Throw again the error by browsing to the MVC View, and open the "Application" tab at the Event Log:



The thousands of events should be sorted by "Date and Time", and you should see your last error at the top of the list:


Double click the error to see its details:


There you can see the URL and the error message, even the line where the exception was thrown.

We hope that this article has helped you ..

Happy programming.....

      by Carmel Schvartzman


כתב: כרמל שוורצמן

Sunday, March 8, 2015

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API

In this article we explain step by step How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API,  to enable Create, Read, Update and Delete (C.R.U.D.) operations using a web service. Also we see how to add this SPA to an MVC Asp.Net C# application. In this post we'll develop from scratch in 30 minutes an entire AngularJS SPA (Single Page App) Application that sends HTTP GET, POST, PATCH and DELETE Ajax Requests to a REST OData Web API.
Our SPA will also be using the responsive Twitter Bootstrap CSS3 styles.
The whole source code for this SPA can be found in the following GitHub repository:

https://github.com/CarmelSoftware/OrchidsSPA

We'll design a unique and single main HTML file (the SPA itself, loading 4 different HTML templates , each one of them corresponding to each one of the CRUD operations)
In this tutorial , we see how to create an AngularJS SPA and, if you want to insert it to an MVC application, just drag and drop the files inside some directory in your MVC. 
This tutorial is a standalone, and allows you to design a SPA in 30 minutes,  copying-pasting code blocks to your own app as you move forward learning. But if you wish to learn AngularJS from 0,  you can take a look at the Tutorials for Absolute Beginners at The AngularJS Club starting at  Lesson #1   .                       

This is a snapshot of the SPA AngularJS that we'll develop from scratch here, in 30 minutes  :

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API




How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API in an MVC App


As you move forward through this Tutorial, you will find the source code to copy-paste to your project, beside every snapshot. That will allow you to go ahead faster.  However,  you can download the complete AngularJS SPA from this GitHub repository, packed all together in a single ZIP file:

https://github.com/CarmelSoftware/OrchidsSPA/archive/master.zip

If you already have an MVC application working, create a folder named "AngularJS", and keep using it as your root directory while creating all the files in this tutorial. We'll focus here in developing the SPA, therefore just copy it inside that folder in your MVC.
We'll use CDN(content delivery network),  to add the references for javascripts and styles,  instead of downloading the files to the project:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API       1



As you see, we add 2 AngularJS scripts, and 2 Bootstrap CSS3 files. Also, we create directories for "Content" and "Controllers".
(copy-paste the source code):
<!doctype html>
<html data-ng-app="OrchidsApp">
<head>
    <title>AngularJS SPA App
    </title>
    <link href="Contents/Style.css" rel="stylesheet" />
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css"  />
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css"  />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular-route.js"></script>
    <script src="/App/Controllers/SPAControllers.js" type="text/javascript"></script>
</head>

Next, copy-paste the CSS3 style to your style.css (placed at the "Contents" folder inside your root directory) file, which we'll be using besides the Bootstrap:
body {background:rgba(255, 238, 238, 0.5);
}
img {width:99%;height:99%;
}
.select
{
width:100px;
padding:5px 5px 5px 25px;
margin:10px 15px 15px 25px;
font:900 12px Comic Sans MS;
opacity:0.9;
background:#f0f0f0;    
border:5px solid #ddd;    
border-radius: 10px;
box-shadow:10px 10px 2px #c0c0c0;
}
.centered
{
 text-align:center;   
}
.div-table{
  display:table;         
  width:auto;         
  background-color:#eee;
  border-spacing:5px; 
}
.div-table-row{
  display:table-row;
  width:auto;
  clear:both;
}
.div-cell-left{
  float:left; 
  display:table-cell;         
  width:33%;
  height:200px;         
  padding:5px 5px 5px 5px;
}
.div-cell-center{
  float:left; 
  display:table-cell;         
  width:56%; 
  height:200px;         
  padding:5px 5px 5px 5px;
}
.div-cell-right{
  float:left; 
  display:table-cell;         
  width:10%; 
  height:200px;         
  padding:5px 5px 5px 5px;
}
.msg {
font:900 Comic Sans MS;
color:#1b42ae;
}

We'll also add a NavBar.html file (again at the Contents folder) containing the Bootstrap NavsBar (for more about adding the Twitter Bootstrap see this Bootstrap Tutorial):
<nav class="navbar navbar-default">
    <div class="container-fluid">

        <div class="navbar-header">
           
            <a class="navbar-brand" href="/">Orchids</a>
        </div>


        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="/">Home <span class="sr-only">(current)</span></a></li>
                <li><a href="#/">Orchids SPA</a></li>
                <li><a href="/PDF">Create PDF</a></li>
                <li><a href="/Home">Help</a></li>
                <li><a href="/Home/About">About</a></li>

            </ul>
            <form class="navbar-form navbar-left" role="search">
                <div class="form-group">
                    <input type="text" class="form-control" placeholder="Search">
                </div>
                <button type="submit" class="btn btn-default">Submit</button>
            </form>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="/Home">Technologies</a></li>

            </ul>
        </div>
        <!-- /.navbar-collapse -->
    </div>
    <!-- /.container-fluid -->
</nav>



As we said, we'll have a unique main HTML file (the SPA itself, and 4 different templates , for  each one of the CRUD operations).At it,  add a <div> element bound to an data-ng-view, and a data-ng-include to insert the NavBar HTML5 inside the web page:
How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API       2





(get the source code):
<body class="container">
    <div data-ng-include="" src="'Contents/Navbar.html'"  ></div>
    <div class="jumbotron">
        <h1>Orchids SPA - AngularJS App</h1>
        
    </div>
    <div id="container">

        <div data-ng-view=""></div>

    </div>  
</body>
</html>
This <div> element will each time be replaced by AngularJS with four Template Views, according to the User's selection ("#/" , "#/add"  ,"#/edit" and "#/delete" ),  that we're designing next: a List of All Items, a template for Creating a new Item, another one for Editing, and one for Deleting it .
We'll first design the List of flowers, so create an "/App/Views/OrchidsList.html" file, and paste the following code inside it:
<div class="jumbotron" >  
    <h2>List of my Favorite Orchids</h2>
    <h4 class="msg">{{Message}}</h4>  
</div>  
<div class="jumbotron">    

        <ul  class="list-group">                
            <li data-ng-repeat="Orchid in OrchidsList" class="list-group-item">  
                <div class="div-table" >
                 <div class="div-table-row">    
                     <div class="div-cell-left">
                        <img src="http://carmelwebapi.somee.com/AngularJS/Contents/Images/{{Orchid.MainPicture}}" alt="{{Orchid.Title}}" title="{{Orchid.Title}}"  > 
                     </div> 
                     <div  class="div-cell-center">          
                        <span >{{Orchid.BlogID}}  .  {{Orchid.Title | uppercase}} <br /><br />
                            {{Orchid.Text}}   {{Orchid.DatePosted | date }}
                        </span>  
                    </div>
                     <div class="div-cell-right">
                         <a href="#/edit/{{Orchid.BlogID}}">
                            <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                         </a>
                         
                     </div>
                  </div>
                </div>
            </li>             
        </ul> 
        <div class="panel panel-default">      
            <div class="panel-body | centered">
                <button data-ng-click="fnShowOrchids(-1)" class="btn btn-default  btn-lg" ><span class="glyphicon glyphicon-hand-left" aria-hidden="true"></span></button>
                <input type="number" data-ng-model="pageSize" max="4" min="1" value="2" class="select"/>
                <button data-ng-click="fnShowOrchids(1)" class="btn btn-default  btn-lg" ><span class="glyphicon glyphicon-hand-right" aria-hidden="true"></span></button>
            </div> 
        </div>  
    <a href="#/add">Add your Favorite Flowers</a>
</div>

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API          3



I've remarked the more relevant code in red. The two buttons at the bottom are for Pagination of the items forward and backwards, by sending to a Controller's method a parameter "+1" or "-1" to add to the current page index. The Input element "number" is for setting the size of the page while paging, so you could state 1 - 2 - 3 etc items per page.
The AngularJS data-ng-repeat Directive is for making a loop through all items in the Flowers collection.
For a deeper understanding of using AngularJS collections and the data-ng-repeat in this Article .
We also add two link buttons to load the "Edit" and the "Delete" HTML View Templates, using the Bootstrap's Glyphicons.
These links send the ID of the selected item to the Edit or the Delete Controllers' Methods at the AngularJS Module.

Next, we're going to code the AngularJS Module, the Routing which glues the HTML Views to its Controllers, and the Controllers themselves.

Therefore, add a javascript  SPAControllers.js file inside a "/App/Controllers/" folder, with an AngularJS Module, as follows:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API          4





var oOrchidsApp = angular.module('OrchidsApp', ['ngRoute','ngResource']);

oOrchidsApp.config(['$routeProvider', function ($routeProvider) {

    $routeProvider

    .when('/',
        {
            templateUrl: "App/Views/OrchidsList.html",
            controller: "OrchidsAllCtl"
        })
    .when('/add',
        {
            templateUrl: "App/Views/OrchidsAdd.html",
            controller: "OrchidsAddCtl"
        })
     .when('/edit/:id',
        {
            templateUrl: "App/Views/OrchidsEdit.html",
            controller: "OrchidsEditCtl"
        })
    .when('/delete/:id',
        {
            templateUrl: "/App/Views/OrchidsDelete.html",
            controller: "OrchidsDeleteCtl"
        })
    .otherwise({ redirectTo: "/" });

}]);



Using an AngularJS $routeProvider , we bind each  View with the correspondent HTML Template and  Controller. Then, when the default "/" is required, the user will be faced with the "All Flowers" HTML  template. IBut if the "/add" page is required, the "OrchidsAdd" template will be displayed.
The same for the "Edit" template.
This way, this is always the same HTML web page which is browsed here. This is a SPA application: everything  is done in THE SAME url, with no reloads of the web page!!!
That's why we needed to reference the angular-route.js AngularJS javascript at the <head> of the main HTML file: to import the $routeProvider at the Module, enabling an SPA application.

We'll also need some Global variables, that is, some data that will be reused by the different Controllers all along our application, and we do not want to repeat several times all over the App:
How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API       5





oOrchidsApp.value('msg', { value: '' });

oOrchidsApp.factory('GlobalSvc', [function () {

    var oFlowersPictures = ["haeckel_orchidae.jpg", "Bulbophyllum.jpg", "Cattleya.jpg", "Orchid Calypso.jpg", "Paphiopedilum_concolor.jpg", "Peristeria.jpg", "Phalaenopsis_amboinensis.jpg", "Sobralia.jpg"];
    var sURLDev = 'http://localhost:21435/WebAPI/OrchidsWebAPI/';
    var sURLProd = 'http://CARMELWEBAPI.SOMEE.COM/WebAPI/OrchidsWebAPI/';
    var bIsDevelopmentTest = false;
    var sURL = bIsDevelopmentTest ? sURLDev : sURLProd;

    return {
        getFlowers: function () { return oFlowersPictures; },
        getURL: function () { return sURL; }

    };
}]);

This "Value" variable will hold a global message which communicates between the different Templates.
That "GlobalSvc" object is an AngularJS Service created in an Factory, and holding the URLs used all over the SPA, and also the list of pictures to be displayed by the Select list that we'll insert in every View Template.
We set two URLs: one for testing purposes, and one for Deployment.

Next, we code an AngularJS Resource, that will allow us sending HTTP GET requests for some  specific ID  :
How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API        6





oOrchidsApp.factory('OrchidsResource', ['GlobalSvc', '$resource',function (GlobalSvc, $resource) {
   
    return $resource(GlobalSvc.getURL() + ":id", { id: "@id" });
}]);





1 - Fetching All Items with HTTP GET:



Now we need the code for loading the View ALL Flowers Template, so add to this Controller to the Module :
oOrchidsApp.controller('OrchidsAllCtl', ['GlobalSvc', '$scope', '$http', '$log', 'msg', function (GlobalSvc, $scope, $http, $log, msg) {

    $scope.angularClass = "angular";
    $scope.OrchidsList = [];
    $scope.pageSize = 2;
    var iCurrentPage = -1; 
     

    $scope.fnShowOrchids = function (direction) {

        iCurrentPage = iCurrentPage + direction;
        iCurrentPage = iCurrentPage >= 0 ? iCurrentPage : 0;
        
        var sURL = GlobalSvc.getURL() +
            "?$skip=" +
            iCurrentPage * $scope.pageSize
            + "&$top=" +
            $scope.pageSize;


        $http.get(sURL).success(function (response) {

            $scope.OrchidsList = response;
            $log.info("OK");

        },function (err) { $log.error(err) })
        
        $scope.Message = "";
        
    }

    $scope.fnShowOrchids(1);
    $scope.Message = msg.value;

}
]);



How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API    7


At this Controller we use the Angular $http Service to send an HTTP GET request, using its get() and success() methods.  We modify the URL from the "GlobalSvc" Service,  and concatenate it according to the OData protocol, using $skip and $top for Paging,  and finally send an HTTP GET request using the $http Service. The success() method is the callback function to be called when the response is 200 (OK). In this method , we populate the list collection with the data from the response.
For a deeper insight about sending HTTP GET Ajax requests , take a look at this article about the $http HTTP GET Service.
That  "direction" argument, is for paging the results backwards ("-1") and forward ("1").
Using Angular Dependency Injection, we got three services : $scope, in order to get the scope variables; $http is for sending requests to the RESTful server; and finally, $log, for logging purposes.
What OData RESTful web service will you use to test your SPA?
If you don't have a web service of your own, you are invited to use the OData RESTful service that I  developed and deployed for you at this URL:

http://carmelwebapi.somee.com/WebAPI/OrchidsWebAPI

You are invited to freely use it. An example of using this OData REST Web API, skipping 2 items and fetching the next 3 ones:

http://carmelwebapi.somee.com/WebAPI/OrchidsWebAPI/?$skip=2&$top=3


As we said before, the $http AngularJS service provide all kinds of HTTP functionality, including sending  HTTP GET, POST, PATCH, PUT or DELETE requests. We will use both the HTTP GET,  the HTTP POST , PATCH and DELETE verbs.
Why we use HTTP PATCH and not HTTP PUT for updating?
Because PUT requires ALL fields to be send to the service, while PATCH accepts partial updates.
Official documentation for the $http service can be learned from the Angular official web site:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API          8




2 - Adding a New Item with HTTP POST:


Now we're designing the AngularJS Template View for creating a new item into the Orchids collection.
Add an "Add" Template to the Views folder, that will contain the following markup:

<div class="container">
<div class="jumbotron">
    <div class="" >    
        <h2>Add your Favorite Orchid</h2>  
    </div> 
        <form name="addOrchid" class=""
            data-ng-submit="fnAdd()">
            <input type="text" class="form-control"
            placeholder="Title"
            data-ng-model="Orchid.Title"
            required>
            <input type="text" class="form-control"
            placeholder="Text"
            data-ng-model="Orchid.Text"
            required>
            <select data-ng-model="Orchid.MainPicture"  title="Select a Picture" data-ng-options="Img for Img in Flowers" class="form-control"></select>
            <input type="submit" class="btn btn-default  btn-lg"
            value="Add"
            data-ng-disabled="addOrchid.$invalid">
            <span>{{fnShowMsg()}}</span>
        </form>
    <a href="#/">See All Flowers</a>
</div>
</div>
How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API         9


As you can see, we bind the Form markup to the properties of an "Orchid" object. The submit button will trigger the fnAdd() Controller method, which we'll code inside the .js file into a new Controller , aimed to add a new item :

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API           10





(get the source code):
oOrchidsApp.controller('OrchidsAddCtl',
    ['GlobalSvc', '$http', '$scope', '$location', '$log', 'msg',
        function (GlobalSvc, $http, $scope, $location, $log, msg) {
            msg.value = "";
            $scope.Flowers = GlobalSvc.getFlowers();
            
            $scope.fnAdd = function () {

                var oFlower = { "Title": $scope.Orchid.Title, "Text": $scope.Orchid.Text, 
                               "MainPicture": $scope.Orchid.MainPicture };    

                $http({
                    url: GlobalSvc.getURL(),
                    method: "POST",
                    data: oFlower, 
                    headers: { 'Content-Type': 'application/json' }
                }).success(function (data, status, headers, config) {
                    msg.value = "New Orchid saved";
                    $scope.IsSaved = true;
                }).error(function (err) {
                     $log.error(err);
                });                
            }

            $scope.fnShowMsg = function () { return msg.value; }
            
}
]);

As before, we use the $http service to send a request, that in this case will be an HTTP POST, which will be interpreted by the REST web service as an adding request.



3 - Editing an Item with HTTP PATCH:



Next, we'll create the markup for the "Edit" View Template at our SPA. Add to the "Views" folder a new HTML file called "OrchidsEdit.html" and type the following markup:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API        11



<div class="container">
<div class="jumbotron">
    <div class="" >    
        <h2>Edit your Favorite Orchid</h2>  
    </div> 
        <form name="editOrchid" class=""
            data-ng-submit="fnEdit()">
            <input type="text" class="form-control"
            placeholder="Title"
            data-ng-model="Orchid.Title"
            required>
            <input type="text" class="form-control"
            placeholder="Text"
            data-ng-model="Orchid.Text"
            required>
            <select data-ng-model="Orchid.MainPicture"  title="Select a Picture" data-ng-options="Img for Img in Flowers" class="form-control"></select>
            <input type="submit" class="btn btn-default  btn-lg"
            value="Update"
            data-ng-disabled="editOrchid.$invalid">
            <span>{{fnShowMsg()}}</span>
        </form>
    <a href="#/">See All Flowers</a>
    
</div>
    </div>
Again as before, we bind all HTML5 elements to the "Orchid" object. Also, we have here a "select" element that allows the user to choose a picture for the post. This select will be loaded at a new Controller that you will append to the javascript Module as follows:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API          12



oOrchidsApp.controller('OrchidsEditCtl',
    ['OrchidsResource', 'GlobalSvc', '$http', '$routeParams', '$scope', '$location', '$log', 'msg',
        function (OrchidsResource, GlobalSvc, $http, $routeParams, $scope, $location, $log, msg) {

    msg.value = "";
    $scope.Flowers = GlobalSvc.getFlowers();
    $scope.Orchid = OrchidsResource.get({ id: $routeParams.id });

    $scope.fnEdit = function () {
                
        var oFlower = { "BlogId": $routeParams.id , "Title": $scope.Orchid.Title, 
                       "Text": $scope.Orchid.Text, "MainPicture": $scope.Orchid.MainPicture };
        
        $http({
            url: GlobalSvc.getURL() + $routeParams.id,
            method: "PATCH",
            data: oFlower,
            headers: { 'Content-Type': 'application/json' }

        }).success(function (data) { msg.value = "Orchid successfully updated"; }).error(function (err) { });

        
    }
    
    $scope.fnShowMsg = function () { return msg.value; }
}
]);

Here are however some new things that weren't there at the previous Controllers, and that will call your attention. For instance,  the $routeParams.id : this service will allows us to get the ID of the chosen item to be edited.
Also , the OrchidsResource's  get() method is used to send an HTTP GET request and to get the specific data for the item that we're going to edit.
Then as before, we use the $http to send this time an HTTP PATCH request. If the response is successful ("success"), we output in the "msg" value some feedback to the user. Because this "msg" variable is a Global one, it will also allows us to show a message at the List View Template (remember: this "List" View has another different $scope at all, so a variable from this "Edit" Controller will not be seen at the "List" Controller).

Save and run your SPA:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API            13




Click the "Add" link to be shown the "Add" template, which will look this way:


How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API            14



Here you will create a new item. The list of pictures is shown at this drop down list:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API          15


Save a new flower, to test how it works:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API       16


If  the web service is working properly, after a sec you should see the "New orchid saved" message  .
Now, go back to the "See all flowers" template View , and remember, since this is a SPA , we're actually located at the SAME html web page.

At the List, we should see the new created item added to the collection. By clicking the "Edit" icon, we'll edit it:
How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API        17



Take a looka at the URL : it contains the ID of the item being edited. Proceed to make some editing, and click the "Update" button, thus sending an HTTP PATCH request to the OData REST service:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API      18



By now, we have added and edited product items. In case that you get no response from the web service, check at the Developer's Tools (pressing the F12) , at the "Network" tab, for the response status. If there is some error , take a look at this HTTP Error Tutorial.
If everything is OK, check for the edited item at the items List, and notice the "Msg"  displayed there.   





4 - Deleting  an Item with HTTP DELETE:



Finally, we build the support for deleting an item:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API      19





We'll add the "Delete" functionality to our SPA application,  by coding a new View template called OrchidsDelete.html as follows:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API             20




<div class="container">
<div class="jumbotron">
    <div class="" >    
        <h2>Delete this Orchid</h2>  
    </div> 
        <form name="deleteOrchid" class=""
            data-ng-submit="fnDelete()">
            <input type="text" class="form-control"
            placeholder="Title"
            data-ng-model="Orchid.Title"
            disabled>
            <input type="text" class="form-control"
            placeholder="Text"
            data-ng-model="Orchid.Text"
            disabled>
            <input  data-ng-model="Orchid.MainPicture"  
                class="form-control" 
                disabled/> 
            <input type="submit" class="btn btn-default  btn-lg"
            value="Delete"
            data-ng-disabled="fnDisable()" ><span>&nbsp;{{fnShowMsg()}}</span>
        </form>
    
    <a href="#/">See All Flowers</a>
</div>
    </div>

Notice that all fields are read-only on this View. We do not allow here to edit the item.

At the javascript file, we append a fourth Controller, for the delete functionality:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API        21




oOrchidsApp.controller('OrchidsDeleteCtl',
    ['OrchidsResource', 'GlobalSvc', '$http', '$routeParams', '$scope', '$location', '$log', 'msg',
        function (OrchidsResource, GlobalSvc, $http, $routeParams, $scope, $location, $log, msg) {

            msg.value = "";
            $scope.isDisabled = false;
            $scope.Orchid = OrchidsResource.get({ id: $routeParams.id });    

            $scope.fnDelete = function () {

                $http(
                {
                    url:  GlobalSvc.getURL() + $routeParams.id,
                    method:"DELETE"

                }
                ).success(function (response) {
                    msg.value = "Orchid successfully deleted";
                    $scope.isDisabled = true;
                }).error(function (err) {  $log.error(err); });
               
            }

            $scope.fnDisable = function () { return $scope.isDisabled;}

            $scope.fnShowMsg = function () { return msg.value; }


}]);





As you can see here,  we call the AngularJS $resource to get the data for displaying the selected item. We do this by sending an HTTP GET  request to the service, containing the ID of the item.
When the submit button is clicked, we send an HTTP DELETE request by using the Angular $http service inside the Controller  . 
The $routeParams service instantiated by Dependency Injection is for getting the ID of the item for deleting.
In addition, we add this methods: the fnDisable() one is to disable the submit button only in the case that the response has been successfully received. Therefore, the user cannot try to erase the same item twice.
And the fnShowMsg() method is to display a message to the user.
To check this functionality, go to the Main web page, and click over the Bootstrap's "delete" icon:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API         22





The Delete View will be displayed as follows:
How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API         23



Click on the "Delete" button, and wait some secs for the success message:

How to Design an AngularJS SPA with CRUD operations for OData RESTful Web API         24


The item has been erased.
Try to remember using the $log functionality in your SPA, to send to yourself messages from your AngularJS app.

That's All!!! You have built an entire SPA application, with support for full CRUD functionality,  connected to an OData RESTful Web API service, and using AngularJS Dependency Injection, Factories, Services, Resources, Controllers, Collections, data binding and Route Provider Services .

Enjoy AngularJS, and Happy Programming ....

      by Carmel Schvartzman


             



כתב: כרמל שוורצמן