Domanda

I'm using AngularJS on a web project and I am noticing that almost all of my form controllers are looking the same. The only difference with the login controller (shown below) and say my reset password controller is $scope.loginForm.$invalid would be $scope.resetForm.$invalid and I would inject and use the ResetService instead of the AuthService.

angular.module('app').controller('LoginCtrl', function ($scope, AuthService) {

  // Form input data
  $scope.formData = {};

  // Are we in the middle of a submit process?
  $scope.busy = false;

  // Has the form been submitted yet?
  $scope.submitted = false;

  // Attempt to submit form via AJAX
  $scope.submit = function (actionUrl) {

    $scope.busy = true;
    $scope.submitted = true;

    // Invalid, activate form and return
    if ($scope.loginForm.$invalid) {
      $scope.busy = false;
      return;
    }

    // Submit data via AJAX
    AuthService.login(actionUrl, $scope.formData).error(function () {
      $scope.busy = false;
    });

  };

});

Obviously, this doesn't feel very DRY, and I am assuming that there is an Angular feature or pattern to extract this similar functionality out?

È stato utile?

Soluzione

A FormCtrl controller was created with all of the functionality. The 2 items that could vary from form to form are the form name attribute and the AJAX method on the service being used for the form, so I pass those two params in the function after the $scope. I then refactored the code a tad to make use of those variables I passed into the function.

The only thing that needs to happen in the LoginCtrl (or any other form controller that implements this) is to instantiate the FormCtrl and pass it the $scope, form name attribute and finally the service method that is used to make the AJAX request.

login.html

<form ng-controller="LoginCtrl"
      ng-submit="submit('my-ajax-url.php')"
      name="loginForm">
  ...
</form>

FormCtrl.js

angular.module('app').controller('FormCtrl', function ($scope, formName, ajaxFunction) {

  // Form input data
  $scope.formData = {};

  // Are we in the middle of a submit process?
  $scope.busy = false;

  // Has the form been submitted yet?
  $scope.submitted = false;

  // Attempt to submit form via AJAX
  $scope.submit = function (actionUrl) {

    $scope.busy = true;
    $scope.submitted = true;

    // Invalid, activate form and return
    if ($scope[formName].$invalid) {
      $scope.busy = false;
      return;
    }

    // Submit data via AJAX
    ajaxFunction(actionUrl, $scope.formData).error(function () {
      $scope.busy = false;
    });

  };

});

LoginCtrl.js

angular.module('app').controller('LoginCtrl', function ($scope, $controller, AuthService) {

  // Instantiate form controller
  $controller('FormCtrl', {
    $scope: $scope,
    formName: 'loginForm',
    ajaxFunction: AuthService.login
  });

});

Altri suggerimenti

Look at what we have here:

angular.module('app').controller('LoginCtrl', FUNCTION)

You can generate these functions from a factory.

In angular, you can add a $inject variable to a function with value being an array of names to injected. Ex:

functionName.$inject = ['$rootScope'];

When that function is called by angular, $rootScope will be injected. So you can pragmatically inject service to functions.

angular.module('app')
    .controller(
        'LoginCtrl',
        ControllerFactory.createSubmitController(function(){}, ['$scope', 'AuthService'])
    )

Inside of the createSubmitController, you create a wrapper function, with all the required names injected. Augment the $scope with the functionalities you like, then call the 1st parameter function with all the injected name as well as the augmented $scope.

That way you should be able to have the same amount of flexibility, with a good baseline of default behaviors.

You can also invoke injector manually in the factory, so you only need to pass in a function like:

function($scope, AuthService)

That's up to you.

See $inject Annotation

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top