How to refactor angularjs controllers with mostly common code
Asked Answered
M

2

7

I'm relatively new to angularjs. I've got some code (HTML + JS) that allows a user to add and remove entries from an in-scope array. Right now however I am massively repeating code for different arrays. I know this can be re-factored but I'm not sure of the angular approach, other than the fact that I'll probably want to use a directive. Any help greatly appreciated.

The HTML

<div class="control-group" ng-class="{error: form.profile.seeking.$invalid}" ng-controller="SeekingCtrl">
    <label class="control-label" for="profile.seeking">Seeking</label>
<div class="controls">
    <ul ng-repeat="seeks in profile.seeking">
      <li>{{seeks}} <button class="btn" ng-click="removeSeeks()">Remove</button></li>
    </ul>
    <input type="text" ng-model="newseeks" id="profile.seeking">
    <button class="btn" ng-disabled="!newseeks" ng-click="addSeeks()">Add new</button>
</div>

<div class="control-group" ng-class="{error: form.project.offering.$invalid}" ng-controller="OfferingCtrl">
<label class="control-label" for="project.offering">Offering</label>
<div class="controls">
    <ul ng-repeat="offer in project.offering">
      <li>{{offer}} <button class="btn" ng-click="removeOffer()">Remove</button></li>
    </ul>
    <input type="text" ng-model="newoffer" id="project.offering">
    <button class="btn" ng-disabled="!newoffer" ng-click="addOffer()">Add new</button>
</div>

The Javascript

var SeekingCtrl = function($scope) {
$scope.addSeeks = function() {
    $scope.profile.seeking = $scope.profile.seeking || [];
    $scope.profile.seeking.push($scope.newseeks);
    $scope.newseeks = "";
};

$scope.removeSeeks = function() {
    $scope.profile.seeking = _.without($scope.profile.seeking, this.seeks);
};
};

var OfferingCtrl = function($scope) {
$scope.addOffer = function() {
    $scope.project.offers = $scope.project.offers || [];
    $scope.project.offers.push($scope.newoffer);
    $scope.newoffer = "";
};

$scope.removeOffer = function() {
    $scope.project.offers = _.without($scope.project.offers, this.offer);
};
};
Marxist answered 12/4, 2013 at 14:23 Comment(2)
Place the code that is used by more than one controller in a Serviceclass, typically called service.js in angular. And inject the service in your controller.Joey
Thanks @TorAndersson. I'm looking for a little more detail if possible, regarding the actual 'how'. I know about services, I use them elsewhere. Right now, I'm just stuck on how to use a service. What would it look like? How would I ensure the correct array was updated?Marxist
M
8

Finally figured this out.

The HTML

<div list-editor list="profile.skills" label="Skills">
</div>

The Directive

<div class="control-group" ng-controller="ListEditorCtrl">
    <label class="control-label" for="{{list}}">{{label}}</label>
    <div class="controls">
    <ul ng-repeat="item in list">
        <li>{{item}} 
            <button class="btn" ng-click="removeItem()"><i class="icon-remove"></i></button>
            <button class="btn" ng-show="!$first" ng-click="moveUpItem()"><i class="icon-chevron-up"></i></button>
            <button class="btn" ng-show="!$last" ng-click="moveDownItem()"><i class="icon-chevron-down"></i></button>
        </li>
    </ul>
    <input type="text" ng-model="newitem" id="{{list}}" />
    <button class="btn" ng-disabled="!newitem" ng-click="addItem()">Add new</button>
</div>

p4pApp.directive('listEditor', function () {
    return {
        restrict: 'A',
        scope: {
            list: '='
        },
        templateUrl: '/templates/common/list_editor_directive.html',
        link: function(scope, element, attrs) {
            scope.label = attrs.label;
        }
    };
}
);

The Controller

var ListEditorCtrl = function($scope) {
$scope.addItem = function() {
    $scope.list = $scope.list || [];
    $scope.list.push($scope.newitem);
    $scope.newitem = "";
};

$scope.removeItem = function() {
    $scope.list.splice(_.indexOf($scope.list, this.item),1);
};
};
Marxist answered 28/5, 2013 at 10:4 Comment(0)
G
0

your question is not clear enough for me it would be better if you put a plunker with an example code that would be easier to understand, but i'll try to answer from what I understand.
In you services.js file do something like this:

app.factory('Seeks', ['$http', function($http){
  var Url  = "seeks.json";
  var Seeks = $http.get(Url).then(function(response){
     return response.data;
  });
  return Seeks;
}]);
app.factory('Offers', ['$http', function($http){
  var Url  = "offers.json";
  var Offers = $http.get(Url).then(function(response){
     return response.data;
  });
  return Offers;
}]);

in your controllers.js do something like this:

var SeekingCtrl = function($scope, Seeks) {
  Seeks.then(function(data){
    $scope.seeks = data;
  });
  $scope.addSeeks = function(newseeks) {
      $scope.seeks.push(newseeks);
      newseeks = "";
  };

  $scope.removeSeeks = function(seek) {
      $scope.seeks = _.without($scope.seeks, seek);
  };
};

var OfferingCtrl = function($scope, Offers) {
  Offers.then(function(data){
    $scope.offers = data;
  });
  $scope.addOffer = function(newoffer) {
      $scope.offers.push(newoffer);
      newoffer = "";
  };

  $scope.removeOffer = function(offer) {
      $scope.offers = _.without($scope.offers, offer);
  };
};

And don't forget to inject the Offers and the Seeks into the controllers in the app.js file like this:

SeekingCtrl.$inject  = ['$scope', 'Seeks'];
OfferingCtrl.$inject = ['$scope', 'Offers'];

update:

just put everything in one controller like this:

var MainCtrl = function($scope, Seeks, Offers) {
  Seeks.then(function(data){
    $scope.seeks = data;
  });
  Offers.then(function(data){
    $scope.offers = data;
  });
  $scope.addSeeks = function(newseeks) {
      $scope.seeks.push(newseeks);
      newseeks = "";
  };
  $scope.addOffer = function(newoffer) {
      $scope.offers.push(newoffer);
      newoffer = "";
  };
  $scope.removeSeeks = function(seek) {
      $scope.seeks = _.without($scope.seeks, seek);
  };
  $scope.removeOffer = function(offer) {
      $scope.offers = _.without($scope.offers, offer);
  };
};

update 2:

var MainCtrl = function($scope, Seeks, Offers) {
  Seeks.then(function(data){
    $scope.seeks = data;
  });
  Offers.then(function(data){
    $scope.offers = data;
  });
  $scope.add = function(item, type) {
      $scope[type].push(item);
      item = "";
  };
  $scope.remove = function(item, type) {
      $scope[type] = _.without($scope[type], item);
  };
};

And the type could be seeks or offers.
For the services that's out of my league.
Hope this help.

Gasparo answered 12/4, 2013 at 21:12 Comment(3)
Hi, thanks for your answer, but I'm really looking to reduce the number of controllers. I don't see why the two (and actually, there are more that I didn't bother showing as they're practically identical) can't be reduced to one controller.Marxist
Thanks for the update but my goal was to reduce the amount of common code. The common code is still there in your solution.Marxist
OK, for the services part there's probably a solution I can't think of it now, but for the add and remove functions you can do something like in the update 2.Gasparo

© 2022 - 2024 — McMap. All rights reserved.