0

I am using angular ui for bootstrap for its modals:

http://angular-ui.github.io/bootstrap/#/modal

I am opening a modal with a controller and templateUrl with:

var modalInstance = $uibModal.open({
    animation: true,
    templateUrl: $scope.templateUrl,
    controller: $scope.controller,
    size: 'lg',
    resolve: {
        formModel: item
    }
});

where formModel is the model I will use in the modal. Here is the controller for the modal:

app.controller('commentCtrl', ['$scope', '$modalInstance', 'formModel', function ($scope, $modalInstance, formModel) {
    $scope.formModel = {};
    var loadFormModel = function () {
        if (formModel !== undefined) {
            $scope.formModel = formModel;
        }
    };
    loadFormModel();
}]);

This modal has child directives and needs to pass properties of formModel to them

template:

<div>
    <child model="formModel.Comment"></child>
</div>

but child is created before the modal's controller has loaded formModel. Inside the child I want to use model as:

app.directive('child', function () {
    return {
        restrict: 'E',
        template: '<textarea ng-model="model"></textarea>',
        link: linkFn,
        controller: controllerFn,
        scope: {
            model: '='
        }
    };
});

Edit:

I've found that I can do:

<div>
    <child model="formModel" property="Comment"></child>
</div>

...

app.directive('child', function () {
    return {
        restrict: 'E',
        template: '<textarea ng-model="model[property]"></textarea>',
        link: linkFn,
        controller: controllerFn,
        scope: {
            model: '=',
            property: '@'
        }
    };
});

Is there a better way to do this without the extra attribute?

Edit 2:

I have found where the bug is:

http://plnkr.co/edit/kUWYDvjR8YArdqtQRHhi?p=preview

See fItem.html for some reason having any ng-if causes the binding to stop working. I have put a contrived ng-if='1===1' in for demonstration

tic
  • 2,484
  • 1
  • 21
  • 33
  • What exactly happens with two-way binding in your case when you do model="formModel.Comment"? Please, provide [MCVE](http://stackoverflow.com/help/mcve). – Estus Flask Nov 23 '15 at 19:37
  • @estus I have added a Plunker. `ng-if` seems to break the binding – tic Dec 03 '15 at 17:14

2 Answers2

1

This happens because ng-if directive creates new inherited scope, so the bug is just a common scope prototypal inheritance pitfall.

The most concise way to get around it is to use controllerAs syntax in conjunction with bindToController, the avoidance of undesirable scope inheritance side effects is the most common use case for them. So it will be

app.directive('fItem', function () {
    return {
        restrict: 'E',
        replace: true,
        templateUrl: 'fItem.html',
        controller: ['$scope', function ($scope) {
        }],
        controllerAs: 'vm',
        bindToController: true,
        scope: {
            model: '='
        }
    };
});

and

<div class="input-group">
    <textarea ng-if='1===1' ng-model="vm.model" class="form-control"></textarea>
</div>
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Very interesting. I will need to do more reading into this to fully understand it, but yes, Now adding `vm` before all of my scope properties fixes this issue. Thank you – tic Dec 03 '15 at 17:43
  • 1
    @tic There's a load of questions on prototype inheritance, I suggest to check this one http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs , it is quite prominent. – Estus Flask Dec 03 '15 at 18:11
0

Not sure what you are trying to do. If you need just to wait until variable is resolved, you need just use promise: (Here is simple one using $timeout, if you use i.e. $http - you ofc dont need $q and $timeout)

  resolve: {
    something: function () {

      return $q(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello, world!');
        }, 3000);
      });

Then modal will be opened only after promise is resolved. http://plnkr.co/edit/DW4MzIO4ej0JorWRgWIK?p=preview

Also keep in mind that you can wrap you object, so if in scope you have $scope.object = {smth : 'somevalue'} :

  resolve: {
    object: function () {
        return $scope.object;  
    });

And in modal controller:

$scope.object = object;

Now object in initial controller scope and object in modal scope point to same javascript object, so any time you change one - another changes. You are free to use object.smth in modal template as usual property. And as soon as it will change you will see changes.

Petr Averyanov
  • 9,327
  • 3
  • 20
  • 38
  • Thanks for your suggestions. I've created an entire skeleton Plunker of the part I am working on, and it **is** working. I have no idea why it is failing in my application. http://plnkr.co/edit/kUWYDvjR8YArdqtQRHhi?p=preview The model binding in fItem is not working, and no changes I make will be reflected in my controller (This is working in the Plunker). The example allows to modify existing entries and add new ones. – tic Nov 23 '15 at 22:09
  • I have added a Plunker to the original post. `ng-if` seems to break the binding. – tic Dec 03 '15 at 17:15