Creating concise AngularJS Controllers for form functionality

While working on a web project with AngularJS, I've observed that most of my form controllers share similar structures. For instance, the only difference between my login controller (as shown below) and a reset password controller is that instead of $scope.loginForm.$invalid, I'd have $scope.resetForm.$invalid. Additionally, I would be using ResetService rather than AuthService.

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

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

  // Check if submit process is ongoing
  $scope.busy = false;

  // Determine if form has been submitted
  $scope.submitted = false;

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

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

    // If form is invalid, return without submitting
    if ($scope.loginForm.$invalid) {
      $scope.busy = false;
      return;
    }

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

  };

});

It's quite evident that this code repetition violates the DRY principle. I presume there must be an Angular functionality or design pattern to refactor out this common functionality?

Answer №1

A controller called FormCtrl was developed to encompass all the necessary functionality. The unique aspects across forms are the form name attribute and the AJAX method used by the service, which are passed as parameters in the function after the $scope. To utilize these variables, I made some adjustments to the code.

In order for the LoginCtrl (or any other form controller that utilizes this) to function properly, it only needs to instantiate the FormCtrl and supply it with the $scope, form name attribute, and the service method used for making 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 = {};

  // Check if a submit process is in progress
  $scope.busy = false;

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

  // Try submitting the form via AJAX
  $scope.submit = function (actionUrl) {

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

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

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

  };

});

LoginCtrl.js

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

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

});

Answer №2

Take a look at this interesting information:


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

You have the ability to create these functions using a factory.

In the world of angular, you can include a $inject variable in a function with an array of names to be injected. For example:

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

When angular calls that function, $rootScope will be injected. This allows you to dynamically inject services into functions.

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

Within the createSubmitController method, you craft a wrapper function with all the necessary injections. Enhance the $scope with desired functionalities, then execute the first parameter function with all injections and the enhanced $scope.

This approach provides flexibility while maintaining a solid foundation of default behaviors.

You also have the option to manually trigger injection within the factory, requiring only a function like:

function($scope, AuthService)

The choice is yours.

Refer to $inject Annotation for more details.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unpacking a props object results in an undefined value

I've been struggling to set up a data-grid in react because I'm facing issues with accessing the data from my props. Whenever I try to access or destructure the prop, it shows up as "undefined" in my console. This problem only arises when the p ...

What is the best way to format a time in a 12-hour clock using JavaScript?

Is it possible to create a timer that performs an action after 12 hours? I want the timer to start at 6am and end at 6pm, lasting for exactly 12 hours. Additionally, I'm interested in converting my current 12-hour clock into a 24-hour format. I am se ...

Learn how to easily incorporate a drop-down list into the material-UI Search component within the navbar to enhance the search results

I integrated a Material UI search bar into my React app's navbar following the instructions from the official documentation on MUI. However, the article does not provide any guidance on how to add a dropdown list when selecting the search input field. ...

transfer information between different express middleware functions within a universal react application

I am working on an isomorphic react app and I am looking for a way to pass state between express middleware functions. One of my express routes handles form submission: export const createPaymentHandler = async (req: Request, res: Response, next: NextFun ...

Having trouble accessing session value within an AngularJS file

Is there a recommended method for accessing session values from an angularjs file that's written in asp.net(C#)? I attempted to use the following code but was unsuccessful: $session.get(key) ...

What is the process for confirming the authenticity of lengthy passwords with bcrypt?

"I encountered a problem that I just can't seem to solve. I set up an authentication flow using JWT with access and refresh tokens. The refresh tokens expire after a long time period, and they can be reset to prevent unauthorized use of stolen refresh ...

Creating a webpage that dynamically loads both content and video using HTML and Javascript

I designed a loading page for my website, but I could use some assistance. The loading page remains visible until the entire HTML content is loaded and then it fades out. However, I am facing an issue because I have a background video that I want to load ...

How can I choose records from collection 'x' in mongodb?

I need to fetch all fields from my database using an API call Here is my code: exports.objfields = async (req, res, next) => { try { var db = mongo.connection; var objeto = req.headers.objeto; const result = db.db.collection(objeto).find( ...

What is the best way to extract all image URLs from a website using JavaScript?

There are various methods to retrieve image src urls using JavaScript, such as utilizing document.images or by targeting all img elements and fetching their src attributes. However, I am currently unable to extract the image urls specified within CSS styl ...

Inquiry from a newcomer: ASP.NET with jQuery

I am working on a webform with a file upload button that has some specific requirements. Whenever the file is uploaded using the button, I need the C# code behind to execute first before any jquery functions are called. <script> $(document.read ...

Syntax error occurs while attempting to render the return statement in a React Component

Just starting out with React and encountering a syntax issue that has me stumped. I'm working on a React Component and I thought I had the opening/closing { & }'s correct, but clearly there's something missing based on the error message it&a ...

Obtaining a URL from a parameter

I have a unique situation with one of my parameters in the routing, as it involves an actual URL. router.get('/api/sitemap/:url', function(req, res) { var url = req.params.url; ... } How can I ensure t ...

Enhance the sent server parameters by including extra options in fineuploader

I have successfully implemented file uploads using . Everything works perfectly. I am able to set parameters in the request object to send additional data to the server. However, when I try to add another parameter dynamically using the setParams function ...

Error: JavaScript function call did not provide a return value

Currently, I am in the process of creating a straightforward angular2/expressJS application. Within my expressJS code, I have an HTTP GET router call that retrieves all users from MongoDB and successfully returns them: app.get('/getusers', funct ...

Tips for transforming numerical date data into a string format

Is there a method to convert the numeric month in a table into a string format? <table style="width: 100%;"> <tr> <th>Date</th> <th>Total</th> </tr> <tr> <td id="date ...

The jQuery click event is failing on the second attempt

My PHP code dynamically generates a list, and I want to be able to delete rows by clicking on them. The code works fine the first time, but not the second time. HTML <li> <div class="pers-left-container"> <img src="<?php ech ...

Creating a Dynamic Form with jQuery, AJAX, PHP, and MySQL for Multiple Input Fields

Success! The code is now functional. <form name="registration" id="registration" action="" method="post"> <div id="reg_names"> <div class="control-group"> <label>Name</label> <div class= ...

Error message in console: React Form showing "Form submission canceled due to lack of connection" despite successful submission

I am facing an issue with my form in my React app. Even though the form is successfully submitting data to a list of boxes, I received an error in the console. The error message says: Form submission canceled because the form is not connected In my Rea ...

Instructions for displaying typed chat messages on the screen using socket.io and node.js

I am currently developing a chat application using socket.io and node.js. I have successfully connected the server and both the socket.io and client-side socket.io are also connected. However, when I type a message on the localhost page and hit enter, noth ...

Changing the z-index using createjs

Is there a way to ensure that the dragged item always stays on top when moved? How can I prevent it from getting dragged underneath another object? Can I specify which "sequenceNumbers" should be moved to the top of the sorting order? //++++++++ ...