5

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!

1
  • The way angular was designed is so that all child scopes have direct access to the parent scopes. If you want to be able to access the same functionality on any page then why not have your crud controller be on the wrapper for the section of content that you must manipulate? Commented Oct 1, 2013 at 15:57

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

That is already done in the Controller. but I don't wanna have to write crudAdd and so on for each time I write a button ?
I already tried this that, but what I'm out to avoid is for general CRUD actions I don't wanna have to write the same code in each controller? Hence the approach to .svc and .object(s).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.