Magic CRUD Angular – Avoid writing JavaScript code with mgCrud (Part I) Why?

(Versión en español) I don’t like Javascript … yes. I like the strongly typed languages, like the compilers and love Resharper. Daily work with an application have too many pages, many, the 75% are written using ASP.NET WebForms, I hate it, and the resting 25%, that grow up daily, are written using MVC and TypeScript. I have two .cshtml views and one .ts file for each entity, I use MVVM with knockoutjs, and I have two viewmodels for each entity in the TypeScript file. It may be that the application has 200 pages and, due to that, I have had to use RequireJs to load the script files. But I could see the script files are repetitives, I have inheritance of course but all script files have the same content, the only change is the name of my clases and the name of entity properties. It makes you ask, really I need to write scripts files? Not long ago I was learning AngularJS, I did a training course with Pedro Hurtado (https://twitter.com/_PedroHurtado) and we were seeing an Angular directive that Pedro had coded with Xavier Jorge Cerdá (https://twitter.com/XaviPaper), this directive is mgAjax, and is the main subject of the mgCrud project at GitHub. Basically, this directive abstract you from server negotiations, it call the server and wait the response to move it to the scope. You only have to use the binding in the view to access the data. Imagine somethin like this:

<div ng-controller="getCountController">
  <span>Have {{count}} comments without read</span>
 
</div>
function controller($scope, $http) {
    $http.get("http://server/user/getunreadcommentscount")
        .success(function (data) {
            $scope.count = data;
        });
}
myModule.controller("getCountController", controller);

Is a basic example, is a view that show a data from server, I have to write the javascript code to make the server call and put the data in the scope. if this was always our problem, we could write a generic code for it, we could create a directive called myAjaxGet with this code:

function myAjaxGet() {
    return {
        restrict: 'E',          
        controller: function ($scope, $http, $attrs) {
            $http.get($attrs.path).success(function (data) {
                $scope.value = data;
            });
        }
    }
}
myModule.directive('myAjaxGet', myAjaxGet);

And write this view:

&lt;my-ajax-get path="http://server/user/getunreadcommentscount">
    <span>Have {{value}} comments without read</span>
&lt;/my-ajax-get>

And we can also do it :

&lt;my-ajax-get path="http://server/user/getunreadcomments">
    <ul>
  <li ng-repeat="comment in value">
    <div>
      Unread comment from {{comment.writer}}
    </div>
 
 
    <div>
      {{comment.text}}
    </div>
 
  </li>
 
 
  <hr />
 
</ul>
&lt;/my-ajax-get>

The first sample is too basic, one value only, but using GET you can obtain a only data or can get a list of entities. Our myAjaxGet can get only a data or a list or an entity… If we dedicate more work to this directive it could works fine, but in ours application it’s not all GET. If we want to save data at the server we could write a myAjaxPost directive. It could be something like that:

function myAjaxGet() {
    return {
        restrict: 'E',          
        controller: function ($scope, $http, $attrs) {
            $http.get($attrs.path).success(function (data) {
                $scope.value = data;
            });
        }
    }
}
myModule.directive('myAjaxGet', myAjaxGet);

We could write that:

function myAjaxPost() {
    return {
        restrict: 'E',          
        controller: function ($scope, $http, $attrs, $window) {
            $scope.accept = function() {
                $http.post($attrs.path, $scope.value).success(function () {
                    $window.history.back();
                });             
            };
        }
    }
}
myModule.directive('myAjaxPost', myAjaxPost);

Now we can write a view to save a comment in a forum, for example:

&lt;my-ajax-post path="http://server/comments/">
    <input type="text" ng-model="value.userName" />&lt;/input>
    <input type="text" ng-model="value.text" />&lt;/input>
    <input type="button" ng-click="accept()" />Save&lt;/input>  
&lt;/my-ajax-post>

We have reading y creating data, what about update? we can write a myAjaxPut:

function myAjaxPut() {
    return {
        restrict: 'E',          
        controller: function ($scope, $http, $attrs, $window) {
            $scope.accept = function() {
                $http.put($attrs.path, $scope.value).success(function () {
                    $window.history.back();
                });             
            };
        }
    }
}
myModule.directive('myAjaxPut', myAjaxPut);

And then we can update a comment:

&lt;my-ajax-put path="http://server/comments/1">
    <input type="text" ng-model="value.userName" />&lt;/input>
    <input type="text" ng-model="value.text" />&lt;/input>
    <input type="button" ng-click="accept()" />Save&lt;/input>  
&lt;/my-ajax-put>

Isn’t? ok, ok, we must to read the data before, but we have our myAjaxGet directive, we have it done, what happend if we write that:

&lt;my-ajax-get path="http://server/comments/1">
    &lt;my-ajax-put path="http://server/comments/1">
        <input type="text" ng-model="value.userName" />&lt;/input>
        <input type="text" ng-model="value.text" />&lt;/input>
        <input type="button" ng-click="accept()" />Save&lt;/input>  
    &lt;/my-ajax-put>
&lt;/my-ajax-put>

So far we have myAjaxGet should read the comment from the server and save it in $scope.value, then myAjaxPut when

accept() should send to the server the data content in “value”. Opss!!, nono, sorry, they have different scopes, my-ajax-put will be used into a my-ajax-get, then when myAjaxPut send data to the server he has to send the value on parent scope, we will have that:

function myAjaxPut() {
    return {
        restrict: 'E',          
        controller: function ($scope, $http, $attrs, $window) {
            $scope.accept = function() {
                $http.put($attrs.path, $scope.$parent.value).success(function () {
                    $window.history.back();
                });             
            };
        }
    }
}
myModule.directive('myAjaxPut', myAjaxPut);

And that:

&lt;my-ajax-get path="http://server/comments/1">
    <input type="text" ng-model="value.userName" />&lt;/input>
    <input type="text" ng-model="value.text" />&lt;/input>
    &lt;my-ajax-put path="http://server/comments/1">
        <input type="button" ng-click="accept()" />Save&lt;/input>  
    &lt;/my-ajax-put>
&lt;/my-ajax-put>

Yes, we have it now! But continuing in our efforts to write less code we could see the three directives are very similar, the only change is the http verb and the moment when do the call to server. Then .. Why don’t we write a unique myAjax directive that covert the rest? with only one directive you could do that:

&lt;my-ajax verb="post" path="http://server/comments/">
    <input type="text" ng-model="value.userName" />&lt;/input>
    <input type="text" ng-model="value.text" />&lt;/input>
    <input type="button" ng-click="accept()" />Save&lt;/input>
&lt;/my-ajax>

And that:

&lt;my-ajax verb="get" path="http://server/comments/1">
    <input type="text" ng-model="value.userName" />&lt;/input>
    <input type="text" ng-model="value.text" />&lt;/input>
    &lt;my-ajax verb="put" path="http://server/comments/1">
        <input type="button" ng-click="accept()" />Save&lt;/input>          
    &lt;/my-ajax>
&lt;/my-ajax>

The code of this directive will be easy:

function myAjax() {
    return {
        restrict: 'E',
        controller: function ($scope, $http, $attrs, $window) {
            var verb = $attrs.verb || 'get';
            $scope.doIt = function() {
                $http[$attr.verb]($attrs.path, verb === 'put' ? $scope.$parent.value : $scope.value).success(function (data) {
                    if (verb === 'get')
                        $scope.value = data;
                    else
                        $window.history.back();
                });
            };
            $scope.accept = doIt;
 
            if (verb === 'get'){
                $scope.doIt();
            }
        }
    }
}
myModule.directive('myAjax', myAjax);

With only one directive you have coverted the functionality of a little application. Of course, we have to do more a more things, but if we could do that in a few hours imagine that you can do with more time. That is what mgCrud is about, mgCrud is based in only one directive, mgAjax, with capabilities to cover as many cases as possible to write views and no write code for each view. It is highly scalable and you can define new behaviours and extend the existing. In the second part we see how to use mgCrud and more capabilities.

One comment

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Demuestra que no eres un bot *