1
\$\begingroup\$

In an effort to learn more about AngularJS directives, I decided to create a simple, edit-in-place directive. I built a simple directive that would display a value, along with an edit link and the edit link would toggle visibility of an input and the save/cancel links. From there, I wanted a setup where I could make multiple properties of an object editable, and I wanted to share the link controls and trigger updates to each property together.

I decided to create a wrapper directive to handle the links and ended up with a working version in which I ended up broadcasting events from the parent(wrapper) directive and taking action in the nested directives based on that. I feel like there is probably a better approach, but this is what I was able to make work.

The HTML:

<html ng-app="editInPlaceApp">
<head>
    <script data-require="angular.js@*" data-semver="1.3.6" src="https://code.angularjs.org/1.3.6/angular.js"></script>
    <link rel="stylesheet" href="style.css" />

  </head>

  <body>
    <div ng-controller="MainController">
      <ul>
        <li ng-repeat="p in people">
          <wrap-in-place>
            <value-in-place value="p.fname"></value-in-place>
            <value-in-place value="p.lname"></value-in-place>
          </wrap-in-place>
        </li>
      </ul>
    </div>

    <script src="script.js"></script>
  </body>

</html>

The JavaScript:

  var app = angular.module('editInPlaceApp',['myDirectives']);
  app.controller('MainController', ['$scope', 'PersonService', function($scope, PersonService){
    $scope.people = PersonService.loadPeople();
  }])
  .factory('PersonService', function(){
    var factory = {};

    factory.loadPeople = function(){
      var peeps = [{"id":"abc0", fname:"John", lname:"Doe"},
        {"id":"abc1", fname:"Jane", lname:"Smith"},
        {"id":"abc2", fname:"Bob", lname:"Jones"}];

      return peeps;
    };

    return factory;
  });

  angular.module('myDirectives',[])
  .directive('wrapInPlace', function(){
    return {
      restrict: 'E',
      transclude: true,
      scope:true,
      template: '<span ng-transclude></span> <a href="#" ng-click="edit()" ng-show="!editChildren">edit</a> <span ng-show="editChildren"><a href="#" ng-click="saveChildren()">save</a> <a href="#" ng-click="cancel()">cancel</a></span>',
      link: function($scope, element, attrs){
        $scope.edit = function(){
          $scope.editChildren = true;
          $scope.$broadcast('editChildren',{"val":$scope.editChildren});
        };

        $scope.cancel = function(){
          $scope.editChildren = false;
          $scope.$broadcast('editChildren',{"val":$scope.editChildren});
        };

        $scope.saveChildren = function(){
          $scope.editChildren = false;
          $scope.$broadcast('saveChildren');
        };
      }
    };
  })
  .directive('valueInPlace', function(){
    return {
      restrict: 'E',
      scope: {
        value: '='
      },
      template: '<span ng-show="!editing">{{value}}</span><input type="text" ng-model="updatedValue" ng-show="editing">',
      link: function ( $scope, element, attrs ) {
        $scope.editing = false;
        $scope.updatedValue = $scope.value;

        $scope.$on('editChildren', function(event, args){
          $scope.editing = args.val;
          if(!args.val){
            $scope.updatedValue = $scope.value;
          }
        });

        $scope.$on('saveChildren', function(event, args){
          $scope.editing = false;
          $scope.value = $scope.updatedValue;
        });

      }
    };
  });

The full working example can be found here.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

It is always better to follow component approach in angularjs. Even if your using angularjs prior to version 1.5.x you can use directives to emulate this approach thus making each directive completely isolated manageable code chunk. it is always better to avoid usage of $scope. it leads to creation of scope soup. To avoid this use controllerAs namespace. and rather than broadcasting events which might lead to uncertain behavious use services to enable communication between directives. This will make your code more manageable and readable and in the future you could easily port to angular2 if you want.

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.