chesco
chesco

Reputation: 161

angularjs enable/disable button on a directive

I'm new to Angular and I've been stuck on this for a day already. I have content for a modal being pulled with a directive which contains a simple form. I want to keep end-users from double clicking when submitting the form. So far this is the closets I've gotten. The content and modal pull up ok, when the button is clicked it gets disabled, the ajax calls is performed ok, the modal hides again, but the button remains disabled. When I mention button I refer to the button on the modal content with id="submitTradeForm"

This is the content in the modal

<div class="cdc-modal">

    @using (@Html.BeginForm("_CreateTrade", "Employees", FormMethod.Post, new { @class = "cdc-form", @id = "createTradeForm" }))
    {
        @Html.AntiForgeryToken()
        @Html.ValidationSummary(null, new { @class="text-danger" })

        <div class="form-group">
            @Html.TextBoxFor(m => m.Name, new { @class = "form-control", @placeholder = "trade" })
        </div>

        <div class="modal-footer">
            <input id="submitTradeForm" type="button" ng-click="createTrade()" ng-disabled="isProcessing" class="btn cdc-button" value="Add new trade" />
        </div>
    }

</div>

This is the JS/Angular code

    //********************** ANGULARJS - BEGIN *****************************************//

    var app = angular.module('empApp',[]);

    app.controller('empCtrl', function ($scope) {

    });

    app.directive('empModal',function($compile){
        return {
            restrict: 'EA',
            templateUrl: '@Url.Content("~/Employees/_CreateTrade")',
            replace: true,
            controller: function ($scope) {

            },
            compile: function (tElement, tAttr) {

                var contents = tElement.contents().remove();
                
                var compiledContents;

                return function (scope, iElement, iAttr) {

                    if (!compiledContents) {
                        compiledContents = $compile(contents);
                    }

                    compiledContents(scope, function (clone, scope) {
                        iElement.append(clone);
                    });

                    scope.createTrade = function () {

                        scope.isProcessing = true;

                        var form = $("#createTradeForm");

                        $.ajax({
                            cache: false,
                            async: true,
                            type: "POST",
                            url: form.attr("action"),
                            data: form.serialize(),
                            success: function (data) {

                                $("#createTradeModal").modal("hide");

                                //empty the container
                                $("#trades").html("");

                                var sel = $("#trades");

                                var newCont = "";

                                for (var i = 0; i < data.length; i++) {
                                    sel.append('<option value="' + data[i].TradeId + '">' + data[i].Name + '</option>');
                                }

                                scope.isProcessing = false;
                            }

                        }).complete(function () {
                            scope.isProcessing = false;
                            console.log('completed');
                            console.log(scope.isProcessing);
                        });

                        
                        
                    };

                };

            }
        }
    });

    //********************** ANGULARJS - END   *****************************************//

This is in the main View

<div ng-app="empApp" ng-controller="empCtrl" class="container">
    <div class="row">
        <div class="col-sm-12">
            <h3>Add New Employee</h3>

            @using (Html.BeginForm("Create", "Employees", FormMethod.Post, new { @id = "createEmployeeForm"}))
            {
                    
                @Html.AntiForgeryToken()
                @Html.ValidationSummary(null, new { @class="text-danger" })
                @Html.Hidden("selectedTrades", null, new { @id="selectedTrades" })

                <div class="row" style="margin-bottom:15px;">
                    <div class="form-group col-sm-12">
                        @Html.TextBoxFor(m => m.EmployeeNumber, new { @placeholder = "employee number", @class = "form-control pull-left col-sm-8" })
                        <span style="min-width:160px;margin-top:5px;" class="col-sm-4 cdc-text pull-left">last entered: @lastentered</span>
                    </div>
                </div>
                <div class="form-group">
                    @Html.TextBoxFor(m => m.FirstName, new { @placeholder = "first name", @class = "form-control", @id = "firstname" })
                </div>
                <div class="form-group">
                    @Html.TextBoxFor(m => m.LastName, new { @placeholder = "last name", @class = "form-control", @id = "lastname" })
                </div>
                <div class="form-group">
                    @Html.TextBoxFor(m => m.StoreAs, new { @placeholder = "store as", @class = "form-control", @id = "storeas" })
                </div>
                <div class="row" style="padding-bottom:15px;min-width:320px !important;">
                        <div id="selectContainer" class="col-sm-8 pull-left" style="max-width:280px;">
                            <select id="trades" multiple="multiple">

                                @if (Model.Trades.Any())
                                {
                                    foreach (CDCManagement.Trade trade in Model.Trades)
                                    {
                                        <option value="@trade.TradeId">@Html.Encode(trade.Name)</option>
                                    }
                                }
                            </select>
                        </div>
                        <a href="#" onclick="addTrade()" data-toggle="modal" data-target="#createTradeModal">
                            <span class="glyphicon glyphicon-plus cdc-icon col-sm-4 pull-left cdc-icon-push" style="padding-left:0;"></span>
                        </a>
                </div>
                <div class="form-group">
                    @Html.TextBoxFor(m => m.PhoneNumber, new { @placeholder = "phone number", @class = "form-control" })
                </div>
                <div class="form-group">
                    @Html.TextAreaFor(m => m.Comments, new { @placeholder = "comments", @class = "form-control" })
                </div>
                <div class="form-group">
                    <input id="createButton" type="button" onclick="processForm()" class="btn cdc-button" value="Add New Employee" />
                </div>
                    
            }

        </div>
    </div>

    <!-- modal begin (add title) -->
    <div class="modal fade" id="createTradeModal">
        <div id="modal-dialog" class="modal-dialog">
            <div id="modal-content" class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title">Add New Trade</h4>
                </div>
                <div id="modal-body" class="modal-body">
                    <emp-modal></emp-modal>
                </div>
            </div>
        </div>
    </div>
    <!-- modal end -->

</div>

Upvotes: 0

Views: 2980

Answers (1)

Ed_
Ed_

Reputation: 19098

You're mixing jQuery and angular, and have hit the very common pitfall of using a jQuery event to trigger something in angular, in this case an AJAX response.

Whenever something changes that angular needs to know about, you need to trigger a $digest. There are lots of resources on the web about this, and I strongly recommend you get a good understanding of the angular lifecycle before writing any more code.

To fix your problem, you should simply be able to change the following line:

scope.isProcessing = false;

To

scope.$apply( function() { 
  isProcessing = false;
});

This tells angular you're changing something that it should know about, and it runs its digest loop which updates all of the views accordingly. This will re-enable the button that you previously disabled.

As an aside: I'm not sure if you're refactoring an existing application to use angular, or starting from scratch. Either way, you should try to remove your dependence on jQuery unless there is a specific reason for it. This stackoverflow question is a great reference that should get you thinking about things 'the angular way'. If you can get your head around this, it will pay dividends.

Upvotes: 1

Related Questions