Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - CRUD directive / service / controller patterns

I'm wanting to create some directives that interacts with the parent controller / scope in some ways. I do CRUD in 2 ways: A) Route based where if you're about to edit a item I use $location to change url to a given url B) Same-Page based where if you click edit on a item it sets $scope.template from $scope.tpl, then in the partial I have a ng-hide / show which hides and shows the table view / detail view.

What I want to achieve is maybe to have less code in my directives and maybe use a more service-based approach or hints?

Directives

'use strict';

/* Directives */
var directives = angular.module("app.directives", ["ui"]);


function CrudCtrl($scope, $attrs, $location, $parse) {
    function getScope(scopeName) {
        scopeName = typeof scopeName || "$parent";
        var ngModel = $parse(scopeName, $scope);
        return ngModel($scope)
    }

    function refreshObjects(scopeName) {
        $scope.svc.query($scope.params, function(objects) {
            var parentScope = getScope(scopeName)
            parentScope.objects = objects
        });
    }

    if (!$scope.refreshObjects) {
        $scope.refreshObjects = function() {
            refreshObjects($attrs.modelname)
        }
    }

    if (!$scope.crudAdd) {
        $scope.crudAdd = function() {
            if ($attrs.url) {
                $location.path($attrs.url);
                return;
            } else {
                var parentScope = getScope($attrs.scope);
                parentScope.object = new $scope.svc();
                parentScope.template = parentScope.tpl;
            }
        }
    }

    if (!$scope.crudDelete) {
        $scope.crudDelete = function() {
            /* Fire off a delete and as a callback we update objects */
            $scope.svc.delete({accountId: $scope.account.uuid, id: $scope.object.id}, function() {
                refreshObjects($attrs.scopeName)
            });
        };
    }

    if (!$scope.crudEdit) {
        $scope.crudEdit = function() {
            if ($attrs.url) {
                $location.path($attrs.url);
                return;
            } else {
                var parentScope = getScope($attrs.scopeName);
                parentScope.object = $scope.object;
                parentScope.template = parentScope.tpl;
            }
        };
    }

    if (!$scope.crudSave) {
        $scope.crudSave = function() {
            var params = {}
            params.accountId = $scope.params.accountId
            if ($scope.object.id) { params.id = $scope.object.id }

            $scope.object.$save(params, function() {
                if ($attrs.url) {
                    $scope.back();
                } else {
                    refreshObjects($attrs.scopeName);

                    var parentScope = getScope($attrs.scopeName);
                    parentScope.template = undefined;
                }
            });
        };
    }

    if (!$scope.crudCancel) {
        $scope.crudCancel = function() {
            if (parentScope.template) {
                var parentScope = getScope($attrs.scopeName);
                parentScope.template = undefined;
            } else {
                $scope.back();
            }
        };
    };
};


directives.directive("refresh", function() {
    return {
        restrict: "E",
        replace: true,
        controller: CrudCtrl,
        scope: true,
        link: function(scope, element, attrs) {
            scope.display_text = attrs.text;
        },
        template: '<button class="btn btn-mini btn-primary" ng-click="refreshObjects()"><i class="icon-refresh"></i> Refresh</button>',
    };
});


/* Create something new */
directives.directive("create", function() {
    return {
        restrict: "E",
        replace: true,
        controller: CrudCtrl,
        scope: true,
        link: function(scope, element, attrs) {
            scope.display_text = attrs.text;
        },
        template: '<button class="btn btn-mini btn-success" ng-click="crudAdd()"><i class="icon-plus"></i> {{display_text || "Add"}}</button>',
    };
});


/* Delete button and update objects */
directives.directive("delete", function() {
    return {
        restrict: "E",
        replace: true,
        controller: CrudCtrl,
        scope: true,
        link: function(scope, element, attrs) {
            scope.display_text = attrs.text;
        },
        template: '<button class="btn btn-mini btn-danger" ng-click="crudDelete()"><i class="icon-remove icon-white"></i> {{display_text}}</button>',
    }
});

/* Helper to create a edit button */
directives.directive("edit", function() {
    return {
        restrict: "E",
        replace:  true,
        controller: CrudCtrl,
        scope: true,
        link: function(scope, element, attrs) {
            scope.display_text = attrs.text;
        },
        template: '<button class="btn btn-mini btn-info" ng-click="crudEdit()"><i class="icon-edit"></i> {{display_text || "Edit"}}</a>',
    }
});

/* Save the object and return to the previous page */
directives.directive("save", function() {
    return {
        restrict: "E",
        replace: true,
        controller: CrudCtrl,
        scope: true,
        link: function(scope, element, attrs) {
            scope.display_text = attrs.text;
        },
        template: '<button class="btn btn-success" ng-click="crudSave()"><i class="icon-ok"> {{display_text || "Save"}}</i></a>',
    };
});

/* Cancel the current action */
directives.directive("cancel", function() {
    return {
        restrict: "E",
        replace: true,
        controller: CrudCtrl,
        scope: true,
        link: function(scope, element, attrs) {
            scope.display_text = attrs.text;
        },
        template: '<button class="btn" ng-click="crudCancel()"><i class="icon-remove"></i> {{display_text || "Cancel"}}</button>'
    }
});

An example controller

function BookingCtrl($scope, Booking) {
      $scope.svc = Booking;
      $scope.objects = $scope.svc.query($scope.params);
  }

Then in a partial for a overview I have:

<div ng-hide="template">
<refresh></refresh>
<create url="/{{params.accountId}}/entity/add"></create>

<table class="table table-condensed table-hover">
    <thead>
        <tr>
            <th></th>
            <th>Name</th>
            <th>Description</th>
        </tr>
    </thead>
    <tbody>
        <tr ng-repeat="object in objects">
            <td>
                <delete></delete>
                <edit url="/{{params.accountId}}/category/{{object.resource_id}}"></edit>
            </td>
            <td>{{object.resource_name}}</td>
            <td>{{object.description}}</td>
        </tr>
        </tr>
        </tr>
    </tbody>
</table>
</div>

<ng-show="template" ng-include src="template"></ng-show>

The Detail partial:

<div class="span4">
    <h3>Category: {{category.resource_name}}</h3>
    <form name="detail_form" class="form-horizontal">
        <div class="control-group">
            <label class="control-label"><strong>Name</strong></label>
            <div class="controls">
                <input required ng-model="object.resource_name" placeholder="Name" type="text" class="input-small">
            </div>
        </div>
        <div class="control-group">
            <label class="control-label"><strong>Description</strong></label>
            <div class="controls">
                <textarea ng-model="object.description" placeholder="Description" type="textarea" rows=5></textarea>
            </div>
        </div>
        <div class="control-group">
            <save scope-name="$parent.$parent"></save>
            <cancel scope-name="$parent.$parent"></cancel>
        </div>
    </form>
<pre>form = {{object | json}}</pre>
</div>

This seems excessive to use $parent.$parent if there's a better way to solve this please help me out!

like image 536
Endre Tk Avatar asked Nov 03 '22 14:11

Endre Tk


1 Answers

I would approach this kind of feature doing followings:

  • put $resource into service.
  • use ng-view to bind urls and partials. Use anchors to link to other partials.
  • define controllers for each partials depending on their role. (access $resource via service)

http://angularjs.org/#wire-up-a-backend may be the example.

like image 74
Tosh Avatar answered Nov 11 '22 13:11

Tosh