How to get the form data when the form is in a dir

2019-06-10 07:50发布

问题:

I have this this template:

<div class="modal" id="popupModal" tabindex="-1" role="dialog" aria-labelledby="createBuildingLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                <h4 class="modal-title" id="createBuildingLabel">{{ title }}</h4>
            </div>
            <form data-ng-submit="submit()">
                <div class="modal-body" data-ng-transclude>

                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-ng-click="visible = false">Annuleren</button>
                    <button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-save"></span>Maken</button>
                </div>
            </form>
        </div>
    </div>
</div>

and here's the directive:

app.directive("modalPopup", [function () {
    return {
        restrict: 'E',
        templateUrl: 'Utils/ModalPopup',
        scope: {
            title: '@',
            onSubmit: '&',
            visible: '='
        },
        transclude: true,
        link: function (scope, element, attributes) {
            var container = $("#popupModal");

            scope.submit = function (newGroup) {
                scope.onSubmit(newGroup);
            }

            scope.hide = function () {
                container.modal('hide');
            }

            scope.show = function () {
                container.modal('show');
            }

            scope.$watch('visible', function (newVal, oldVal) {

                if (newVal === true) {
                    scope.show();
                }
                else {
                    scope.hide();
                }
            })
        }
    }
}]);

As you can see I have declared my form tag inside the directive and I also use transclude to determine how my form is going to look like. For now I have this:

<modal-popup title="Nieuwe groep aanmaken" data-on-submit="createGroup()" visible="showAddGroupForm">
    <div class="row">
        <div class="col-md-3">Kopieren van:</div>
        <div class="col-md-8">
            <select class="form-control" data-ng-model="newGroup.Year">
                <option value="">Nieuw jaar</option>
                <option data-ng-repeat="year in years" value="{{year.Id}}">{{year.Name}}</option>
            </select>
        </div>
    </div>
    <div class="row">
        <div class="col-md-3">Naam</div>
        <div class="col-md-8">
            <input type="text" class="form-control" data-ng-model="newGroup.Name" />
        </div>
    </div>
</modal-popup>

When the submit button is pressed, I want the data to be available in my controller.

I ques the data isn't available because of the isolated scope, however I'm not sure. What do I need to do to get the data back from the directive into my controller?

PS: I know about angular-ui and angularstrap, but I'm doing this to learn about Angular.

EDIT:

Here's a Fiddle

回答1:

I think the cause is a misunderstanding about how scopes work (especially with transclusion).

Let's start with this code (from the fiddle):

<div ng-controller="MyCtrl">
    <my-popup on-submit="formSubmitted(model)">
        <input type="text" ng-model="model.Name"/>    
    </my-popup>
</div>

Since <my-popup> transcludes its content, the scope above is that of MyCtrl, even in the content of the directive. By content I mean the <input>, NOT the directive template, i.e. the <div><form ... code.

Therefore it is implied that model (as used in ng-model="model.Name") is a property of the scope of MyCtrl, as is formSubmitted(). Since both are members of the same scope, you do not need to pass the model as argument; you could just do:

(in the template:)

<my-popup on-submit="formSubmitted()"><!-- no `model` argument -->

(the controller:)

function MyCtrl($scope) {
    // I like declaring $scope members explicitly,
    // though it can work without it (charlietfl comments)
    $scope.model = {}; 
    $scope.submittedValue = null;
    $scope.formSubmitted = function() {
        // another bug was here; `model` is always a member of the `$scope`
        // object, not a local var
        $scope.submittedValue = $scope.model.Name;
    }
}

Another bug is in the directive code:

link: function(scope, element, attributes){
    scope.submit = function(){
        scope.onSubmit({model: model});
    }
}

The variable model (not the name model:) is undefined! It is a property of the parent scope, so you would have a chance if the scope was not isolated. With the isolated scope of the directive, it may work with an awful workaround that I am not even considering to write :)

Luckily, you do not need the directive to know about things happening in the external scope. The directive has one function, to display the form and the submit button and invoke a callback when the submit button is clicked. So the following is not only enough for this example, but also conceptually correct (the directive does not care what is happenning outside it):

link: function(scope, element, attributes){
    scope.submit = function(){
        scope.onSubmit();
    }
}

See the fiddle: http://jsfiddle.net/PRnYg/


By the way: You are using Angular v1.0.1. This is WAAAAY too old, seriously consider upgrading!!! If you do upgrade, add the closing </div> to the template: <div ng-transclude></div>.