user21398
user21398

Reputation: 1493

Handling success/error DOM manipulation from Angular controller to directive after POST

I've been learning angular a bit here and there, but I don't quite get what the angular way of handling success/error responses from the server would be. The typical jQuery way to do it would be:

In angular, we have directives that manipulate the DOM and we have controllers that deal with the models. Say we have the following:

form.html

<div ng-controller="myController">
    <span class="hidden success-message"></span>
    <form>
        <label> Name </label>
        <input type="text" name="name" ng-model="name">
        <span class="name-error hidden"></span>
        <label> Occupation </label>
        <input type="text" name="occupation" ng-model="occupation">
        <span class="occupation-error hidden"></span>

        <input submit-directive type="submit" id="submit" value="Submit">
    </form>
</div>

app.js

angular.module('app', []).controller('myController', [$scope, $http, function($scope, $http) {
    $scope.name = "";
    $scope.occupation = "";

    $scope.doSubmit: function(formData) {
        $http.post("/save", formData)
        .success(function(response) {
            $scope.name = response['name']
            $scope.occupation = response['occupation']

            // How to pass success messages back to directive?

        }.fail(function(err) {

            // How to pass error messages back to directive?

        }
    }
});

angular.module('app', []).directive('submit-directive', function() {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
             element.click(function() {
                 // Pass form data back to controller
                 scope.$apply('doSubmit(\"' + $("form").serialize() +'\")');
             });

             // How do I get the success/fail results from the Controller so that I can manipulate the DOM?

        }
    };
});

Here, a user enters their name and occupation. Upon submitting, the directive grabs the form data and presents it to the controller for a POST request. When the response is returned, the directive should either display a success message or display the error messages.

It's not clear how I would pass the response from the controller to the directive for DOM manipulation.

The only way I've thought of is to create a $watch:

// in controller
$scope.isFormPostSuccess = false;
$scope.formResponse = null;
// ....
.success(function(response) {
     $scope.isFormPostSuccess = true;
     $scope.formResponse = response;
}.fail(function(err) {
     $scope.isFormPostSuccess = false;
     $scope.formResponse = err;
}

Then in the directive, I would watch those variables for changes and act accordingly. However, this way of doing it feels very messy and doesn't seem to scale well for larger applications or when having many independent forms. I could also move all the DOM manipulation into the controller, but that's just bad design.

What is the proper angular way?

Upvotes: 1

Views: 1518

Answers (1)

Tong Shen
Tong Shen

Reputation: 1369

Actually, the so-called "Angular way" should be to combine the usage of models($scope.foo) with directives(like ng-model, ng-bind, ng-show, etc., or your custom directives) to achieve the goal of a DOM operation. It's not like to initiate a DOM operation by hand within your controller, but to change your models in the controller and the DOM will update itself accordingly, with the help of directives.

In your case, if you just want to display the success/error message, I don't think you need to use a custom directive.


First, you should wrap related form fields in a single modal object instead of multiple modal objects. Like:

<input type="text" name="name" ng-model="person.name">

and

<input type="text" name="occupation" ng-model="person.occupation">

Secondly, you should use ng-submit or ng-click for the form submitting action, like:

<input type="submit" id="submit" value="Submit" ng-click="doSubmit(person)">

or

<form ng-submit="doSubmit(person)">
    ...
    <input type="submit" id="submit" value="Submit">
</form>

Thirdly, you should use ng-model to get the data out of the form instead of serialize(). Like discussed in How can I post data as form data instead of a request payload? :

$scope.doSubmit = function(formData) {    // use `=` instead of `:` here
    $http({
        method: "POST",
        url: "/save",
        data: $.param(formData),
        headers: {'Content-Type': 'application/x-www-form-urlencoded'}
    })
    // ...
}

And finally, if you want to show some feedback other than text to the user, you should use ng-show, ng-switch, ng-class or ng-style when possible. Like:

<span class="name-error" ng-show="errorMessage.name" ng-bind="errorMessage.name"></span>

And in the fail() callback when you want to display a name error message:

errorMessage.name = "Some error message here.";  // don't forget to initialize errorMessage to an empty object

And that's it!


Update

For the questions you ask in the comments, I think, in Angular, people may prefer using separate cohesive directives for individual DOM elements, or nested directives, and then connecting them with models or controllers(nested directives).

In your example, I would suggest you use a combination of both.

First, use a set of nested directives for the UI purpose. You may have a directive binding to the body tag and accessing viewport info there. Then you can access that in the inside directives with require: '^ParentDirective'.

Then, you can bind your models to the inside directives. Thus, you separate data and UI manipulation.

e.g.

<body auto-size-messages>
    <div ng-repeat="message in messages" message="message"></div>
</body>

In auto-size-messages, you get viewport attributes and assign that to the controller. And in message, you manipulate the DOM to place the message on your desired position and show the content according to the message attribute.

You can refer to the Creating a Directive that Wraps Other Elements section in https://docs.angularjs.org/guide/directive for nested directives.

Upvotes: 3

Related Questions