在AngularJS中思考 - 在AJAX请求中修改DOM的正确方法是什么?

时间:2022-04-14 10:04:36

I'm brand new to AngularJS. I've read over "Thinking in AngularJS" if I have a jQuery background? and the answers made a lot of sense. However, I'm still having a hard time translating what I want to do without relying on jQuery.

我是AngularJS的新手。如果我有jQuery背景,我已经读过“在AngularJS中思考”了吗?并且答案很有意义。但是,我仍然很难在不依赖jQuery的情况下翻译我想做的事情。

My request seems fairly simple. I have a form that does an AJAX call when submitted. I want the Submit button to visually update to inform the user on the the AJAX request status. The Submit button won't be updated with simple text, but instead be updated with text plus a icon. The icon is in HTML which means I can't use AngularJS simple data binding.

我的要求似乎很简单。我有一个表单在提交时进行AJAX调用。我希望“提交”按钮可以直观地更新,以通知用户AJAX请求状态。 “提交”按钮不会使用简单文本进行更新,而是使用文本和图标进行更新。图标是HTML格式,这意味着我无法使用AngularJS简单数据绑定。

I have this request working in jsFiddle.

我有这个请求在jsFiddle中工作。

HTML

HTML

<div ng-app >
    <form id="id_request" ng-controller="RequestCtrl">
        <input id="id_title" name="title" ng-model="request.title" type="text" class="ng-valid ng-dirty" />
        <button type="submit" class="btn btn-primary" ng-click="submitRequest($event)">
            Submit
        </button>
    </form>
</div>

AngularJS

AngularJS

function RequestCtrl($scope, $http) {
    $scope.request = {};
    $scope.submitRequest = function($event) {
        $($event.target).prop("disabled", true).html('<i class="icon-refresh icon-spin"></i> Submitting</a>');
        $http({
            method : 'GET',
            url : '/ravishi/urSDM/3/',
            data : $.param($scope.request),
            headers : {
                'Content-Type' : 'application/x-www-form-urlencoded',
            }
        }).
        success(function(data, status, headers, config) {
            console.log("success");
            console.log($event);
            // This callback will be called asynchronously when the response is available.
            $($event.target).html('<i class="icon-ok"></i> Success').delay(1500).queue(function(next) {
                $(this).removeAttr("disabled").html("Submit");
                next();
            });
        }).
        error(function(data, status, headers, config) {
            console.log("error");
            // Called asynchronously if an error occurs or server returns response with an error status.
            $($event.target).html('<i class="icon-warning-sign"></i> Error').delay(1500).queue(function(next) {
                $(this).removeAttr("disabled").html("Submit");
                next();
            });
        });
    }
}

I am completely aware that this is the wrong way to do it in AngularJS. I shouldn't be updating the DOM in my controller and I should by trying to avoid jQuery completely.

我完全清楚这是在AngularJS中这样做的错误方法。我不应该更新我的控制器中的DOM,我应该尝试完全避免使用jQuery。

From what I gather, I need to use directives. I've seen some examples but can't think of a nice way to update the button's HTML through the multiple states it goes through. There are four states of the button:

从我收集的内容来看,我需要使用指令。我已经看过一些例子,但想不出一个很好的方法来通过它经历的多个状态来更新按钮的HTML。按钮有四种状态:

1 Initial -> 2 Submitting -> 3 Error or 4 Success -> 1 Initial

1初始 - > 2提交 - > 3错误或4成功 - > 1初始

My first thought is to update an arbitrary button attribute in the controller. Then in the directive, I would somehow retrieve the attribute value and conditionally update the HTML appropriately. With this approach, I am still unsure of how to link the status of the AJAX request to the button's HTML in the directive. Should I forgo the AJAX call in the controller and instead do the AJAX call in the directive by binding to the button's click event? Or, is there is a better way to do this?

我的第一个想法是更新控制器中的任意按钮属性。然后在指令中,我会以某种方式检索属性值并有条件地适当地更新HTML。使用这种方法,我仍然不确定如何将AJAX请求的状态链接到指令中的按钮的HTML。我应该放弃控制器中的AJAX调用,而是通过绑定到按钮的click事件来执行指令中的AJAX调用吗?或者,有更好的方法吗?

2 个解决方案

#1


4  

The main change you have to do is to move away from manipulating DOM and rather do all changes on the model itself. Here is an example where I used two separate properties $scope.icon and $scope.locked to control icon style and button state: http://jsfiddle.net/CpZ9T/1/

您需要做的主要改变是远离操纵DOM,而是对模型本身进行所有更改。下面是一个示例,我使用两个单独的属性$ scope.icon和$ scope.locked来控制图标样式和按钮状态:http://jsfiddle.net/CpZ9T/1/

#2


1  

In you case, I think you definitely should create a directive to encapsulate the button with its behavior. Here's an example:

在你的情况下,我认为你肯定应该创建一个指令来封装按钮及其行为。这是一个例子:

HTML

HTML

<div ng-app='app'>
  <form id="id_request" ng-controller="RequestCtrl">
    <input id="id_title" name="title" ng-model="request.title" type="text" class="ng-valid ng-dirty" />
    <my-button state="state" click-fn="submitRequest()"></my-button>
  </form>
</div>

Javascript

使用Javascript

angular.module('app', [])
    .directive('myButton', function() {
    return {
        restrict: 'E',
        scope: { state: '=', clickFn: '&' },
        template: '<button type="submit" class="btn btn-primary" ng-click="clickFn()" ng-disabled="disabled">' +    
                  '  <i ng-class="cssClass"></i>' + 
                  '  {{ text }}' + 
                  '</button>',
        controller: function($scope) {
            $scope.$watch('state', function(newValue) {                                                    
                $scope.disabled = newValue !== 1;
                switch (newValue) {
                    case 1:
                        $scope.text = 'Submit';
                        $scope.cssClass = ''; 
                        break;
                    case 2:
                        $scope.text = 'Submitting';
                        $scope.cssClass = 'icon-refresh icon-spin';  
                        break;
                    case 3:
                        $scope.text = 'Error';
                        $scope.cssClass = 'icon-warning-sign';  
                        break;
                    case 4: 
                        $scope.text = 'Success';
                        $scope.cssClass = 'icon-ok';  
                        break;
                }
            });
        }        
    };
})
.controller('RequestCtrl', function ($scope, $http, $timeout) {
    $scope.state = 1;
    $scope.request = {};

    $scope.submitRequest = function() {
        $scope.state = 2;
        $http({
            method : 'GET',
            url : '/ravishi/urSDM/3/',
            data : $.param($scope.request),
            headers : {
                'Content-Type' : 'application/x-www-form-urlencoded',
            }
        }).
        success(function(data, status, headers, config) {            
            $scope.state = 4;
            $timeout(function() { $scope.state = 1; }, 1500);
            console.log("success");                       
        }).
        error(function(data, status, headers, config) {
            $scope.state = 3;
            $timeout(function() { $scope.state = 1 }, 1500);
            console.log("error");                        
        });
    }
});

jsFiddle here.

jsFiddle在这里。

#1


4  

The main change you have to do is to move away from manipulating DOM and rather do all changes on the model itself. Here is an example where I used two separate properties $scope.icon and $scope.locked to control icon style and button state: http://jsfiddle.net/CpZ9T/1/

您需要做的主要改变是远离操纵DOM,而是对模型本身进行所有更改。下面是一个示例,我使用两个单独的属性$ scope.icon和$ scope.locked来控制图标样式和按钮状态:http://jsfiddle.net/CpZ9T/1/

#2


1  

In you case, I think you definitely should create a directive to encapsulate the button with its behavior. Here's an example:

在你的情况下,我认为你肯定应该创建一个指令来封装按钮及其行为。这是一个例子:

HTML

HTML

<div ng-app='app'>
  <form id="id_request" ng-controller="RequestCtrl">
    <input id="id_title" name="title" ng-model="request.title" type="text" class="ng-valid ng-dirty" />
    <my-button state="state" click-fn="submitRequest()"></my-button>
  </form>
</div>

Javascript

使用Javascript

angular.module('app', [])
    .directive('myButton', function() {
    return {
        restrict: 'E',
        scope: { state: '=', clickFn: '&' },
        template: '<button type="submit" class="btn btn-primary" ng-click="clickFn()" ng-disabled="disabled">' +    
                  '  <i ng-class="cssClass"></i>' + 
                  '  {{ text }}' + 
                  '</button>',
        controller: function($scope) {
            $scope.$watch('state', function(newValue) {                                                    
                $scope.disabled = newValue !== 1;
                switch (newValue) {
                    case 1:
                        $scope.text = 'Submit';
                        $scope.cssClass = ''; 
                        break;
                    case 2:
                        $scope.text = 'Submitting';
                        $scope.cssClass = 'icon-refresh icon-spin';  
                        break;
                    case 3:
                        $scope.text = 'Error';
                        $scope.cssClass = 'icon-warning-sign';  
                        break;
                    case 4: 
                        $scope.text = 'Success';
                        $scope.cssClass = 'icon-ok';  
                        break;
                }
            });
        }        
    };
})
.controller('RequestCtrl', function ($scope, $http, $timeout) {
    $scope.state = 1;
    $scope.request = {};

    $scope.submitRequest = function() {
        $scope.state = 2;
        $http({
            method : 'GET',
            url : '/ravishi/urSDM/3/',
            data : $.param($scope.request),
            headers : {
                'Content-Type' : 'application/x-www-form-urlencoded',
            }
        }).
        success(function(data, status, headers, config) {            
            $scope.state = 4;
            $timeout(function() { $scope.state = 1; }, 1500);
            console.log("success");                       
        }).
        error(function(data, status, headers, config) {
            $scope.state = 3;
            $timeout(function() { $scope.state = 1 }, 1500);
            console.log("error");                        
        });
    }
});

jsFiddle here.

jsFiddle在这里。