6

I am using this FAQ entry to open a modal dialog in a child state of a certain state: https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal-at-a-certain-state

My code is below. When I open a modal dialog, I need to get access to the properties of the parent state's scope. Is this possible?

plnkr: http://plnkr.co/edit/knY87n

         .state('edit', {
             url: '/{id:[0-9a-f]+}',
             views: {
                 '@': {
                     templateUrl: 'views/edit.html',
                     controller: 'editContr'
                 }
             }
         })
         .state('edit.item', {
             url: "/item/new",
             onEnter: function($stateParams, $state, $modal) {
                 $modal.open({
                     controller: 'itemEditContr',
                     templateUrl: 'views/edit-item.html',
                 }).result.then(function (item) {
                     //
                     // here I need to insert item into the items
                     // seen by my parent state. how?
                     //
                     $state.go('^');
                 }, function () {
                     $state.go('^');
                 });
             }
         });


function editContr($scope) {
    $scope.items = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
}

function itemEditContr($scope, $modalInstance) {
     $scope.submit = function () {
         $modalInstance.close($scope.item);
     };
     $scope.cancel = function () {
         $modalInstance.dismiss('cancel');
     };
     $scope.item = {name: 'test'};
}
akonsu
  • 28,824
  • 33
  • 119
  • 194

3 Answers3

5

Are you planning to update params set by the parent? You should be able to get the parent state using

$state.$current.parent

For e.g. the name of the parent state will be $state.$current.parent.name

EDIT: Updating since the OP wanted to access the Scope and not the parent state.

You can emit an event from the child and then capture it in the parent.

Untested code: In Parent:

$scope.$on('ADD_ITEM', function(evt, msg) {
     $scope.items.add(msg);
}

In the child state:

$scope.$emit('ADD_ITEM', item);
rajasaur
  • 5,340
  • 2
  • 27
  • 22
  • thank you. the scope of the parent state is a sibling of the scope of the child state, but I guess I can register my callback on `$rootScope` huh? :) – akonsu Mar 21 '14 at 04:26
  • 1
    Hmm, not sure how its a sibling and not a child. Would it be possible to post a fiddle? – rajasaur Mar 21 '14 at 04:48
  • this is because I am referring to the `then` function called by the promise returned by the `$modal.open`. – akonsu Mar 21 '14 at 12:47
  • please see plnkr here: http://plnkr.co/edit/knY87n it shows the problem. Line 39 prints `scope`, and it is not a child of the parent state's scope. I have left comments there about what I need to do. thanks a lot for your help. – akonsu Mar 21 '14 at 16:40
  • 1
    In your case, there is no controller for the child state, $modal will create a new child scope unless a scope is passed to it. This is the scope for which the parent is a $$prevSibling. If you had a controller defined in your child state, the scopes will be hierarchical. Anyway, I got what you wanted using a service. Create a service that will hold the value of "title" and set that from both the controller as well as teh onEnter block. I have a fiddle I can post that will change the value of title (displayed on the screen) when its loaded and a different title when its closed (or dismissed) – rajasaur Mar 21 '14 at 17:57
  • yeah, I was trying to figure out how to pass a scope to `$modal` but I do not see how I can do that. thanks. I know about a service injection, I just was trying to find a more direct way to do it. Your suggestion with scope events works (I just need to have a listener on `$rootScope`). – akonsu Mar 21 '14 at 18:04
  • you have no need to use `$rootScope`, and it would be bad practice in this case to do so. – Brian Vanderbusch Mar 22 '14 at 03:14
  • @akonsu what was your solution? I am using this modalStateProvider and it is impossible to set the scope to ``$modal.open`` @see http://stackoverflow.com/a/24726331/595152 – Betty St Feb 24 '15 at 14:06
  • @BrianVanderbusch I understand why he needs $rootScope. $modal is going to create a child scope of a provided scope, if no scope provided it uses $rootScope. – Betty St Feb 24 '15 at 14:10
  • @BettySt I was sending events to root scope and I mentioned in the comment above. – akonsu Feb 24 '15 at 14:10
1

long story short, yes it is. Reading the angularjs developer guide for scopes, is actually one of their more helpful and very well documented pieces: http://docs.angularjs.org/guide/scope

Aside from that, scope in angular is no different than scope with any javascript object.

You've got one or two things that you're not doing correctly. For one, passing item in your onEnter function won't help unless you're grabbing something from the url as an identifier, or resloving some data that you can inject into the states controller. You're trying to do the latter, but you aren't resolving anything, so you are getting undefined on item.

One trick you can use is to set a truthy value in your your parent, and access it.

 //in parent ctrl
 $scope.newItem = function(itemname){
     return {name:itemname}
 }

 $scope.save = function(item){
    $scope.items.push(item);
}

Then when you open your modal call $scope.getItem() within the controller instead of injecting item into the controller directly

function itemEditContr($scope, $modalInstance) {
     $scope.submit = function () {
         $modalInstance.close();
     };
     $scope.cancel = function () {
         $modalInstance.dismiss('cancel');
     };
     $scope.item = $scope.newItem('test') //this will look for newItem funciton in the local scope, fail to find it, and walk up the heirarchy until it has found newItem() in the parent

    //now a save function, also defined on the parent scope
    $scope.save($scope.item);


}

Accessing the parent scope is nothing special, just make sure to get a value, and not overwrite it. so you can access $scope.items from the child controller by assigning it to a variable, or you can push it new values, but never set it, or you will create a new local items object on the child scope instead.

Brian Vanderbusch
  • 3,313
  • 5
  • 31
  • 43
  • thank you. the problem is that the `$scope` that I see in `itemEditContr` is a sibling, not a child of the `$scope` seen in `editContr`... I gues I could use `$scope.$$prevSibling` but it looks fragile. – akonsu Mar 21 '14 at 04:25
  • your code is definitely a `child` scope and not sibling scope. using `item.edit` guarantees that the scope inside `itemEditContr` is a child of `editContr`. additonally, that means that angular-ui-router is trying to look for a `ui-view` directive in your template for your `edit` state, in order to view. This may be why you think they are siblings, but they are not. defining the view in the manner you have in the `edit` state is no different than just using template inside the main state config instead of nesting it inside of a view object. – Brian Vanderbusch Mar 21 '14 at 05:19
  • this is because I am referring to the `then` function called by the promise returned by the `$modal.open`. this is not in the controller... – akonsu Mar 21 '14 at 12:47
  • i never said it was. when you define a new state on the stateProvider, and use the `.` and the bit before it is the name of an existing state, then you are defining a child state. and using `@` inside the views config just targets any ui-view inside edit's scopes parent view, which is default behavior. You *have* defined a child state. – Brian Vanderbusch Mar 21 '14 at 14:03
  • please see plnkr here: http://plnkr.co/edit/knY87n it shows the problem. Line 39 prints `scope`, and it is not a child of the parent state's scope. I have left comments there about what I need to do. thanks a lot for your help. – akonsu Mar 21 '14 at 16:40
  • why would you expect console.log to show parent scope? and why wouldn't you use $log instead? I'm sorry, but I'm done helping until you demonstrate you're willing to hear what we're trying to tell you. – Brian Vanderbusch Mar 22 '14 at 02:12
  • this is fine. I need no more help. I never said that I expected it to show the parent scope. You are not hearing what I am saying. – akonsu Mar 22 '14 at 02:50
0

I struggled with this too and found an alternative solution. Instead of using the onEnter function you can use a controller with a very simple view that calls a function to open the modal. I used a

config(['$stateProvider', function($stateProvider) {

            // Now set up the states
            $stateProvider

                    .state('exmple.modal', {
                        url: "/example/modal/",
                        controller: "ExampleController",
                        template: '<div ng-init="openModal()"></div>'
                    });
        }]);        

In the example controller you an put the openModal() function to open the modal where you have a scope.