4

I'm using AngularJS's UI-Router to manage routes for my web application.

I have two states: parent_state and child_state arranged as shown below.

$stateProvider
.state('parent_state', {
    abstract: true,
    views: {
        '@' : {
            templateUrl: 'http://example.com/parent.html',
            controller: 'ParentCtrl'
        }
    }
})
.state('child_state', {
    parent: 'parent_state',
    url: '/child',
    params: {
      myArg: {value: null}
    },
    views: {
      'mainarea@parent_state': {
          templateUrl: 'http://example.com/child.html',
          controller: 'ChildCtrl'
        }
    }
})

From within ChildCtrl, I can access myArg like this:

app.controller("ChildCtrl", function($stateParams) {
    console.log('myArg = ', $stateParams.myArg);
});

Is it possible for me to access myArg and have it displayed in the html page parent.html? If so, how can it be done? I see that the ParentCtrl controller for the abstract state is never even called.

This question addresses a related topic. But it doesn't show me how to display a parameter to the child state in a template of the parent state.

Saqib Ali
  • 11,931
  • 41
  • 133
  • 272
  • Possible duplicate of [angular ui-router and accessing child state params from parent](https://stackoverflow.com/questions/24301754/angular-ui-router-and-accessing-child-state-params-from-parent) – fracz Nov 23 '18 at 16:21
  • Fracz, not quite. I updated my question to explain how it is different. – Saqib Ali Nov 24 '18 at 04:32

3 Answers3

3

The first thing that comes to my mind is to use events for notifying parent after child param change. See the following (you can even run it here).

Child, after rendering, emits an event to the parent with the changed value of the parameter. Parent grabs and displays it in its own template.

angular.module('myApp', ['ui.router'])
.config(function ($stateProvider, $urlRouterProvider) {
  $stateProvider
  .state('parent_state', {
    abstract: true,
    template: "<h1>Parent! Value from child: {{ paramFromChild }}</h1><div ui-view></div>",
    controller: function ($scope) {
      $scope.$on('childLoaded', function (e, param) {
        $scope.paramFromChild = param;
      });
    }
  })
  .state('child_state', {
    parent: 'parent_state',
    url: '/child',
    params: {
        myArg: {value: null}
    },
    template: '<h2>Child! Value: {{ param }}</h2>',
    controller: function($stateParams, $scope){ 
      $scope.param = $stateParams.myArg;
      $scope.$emit('childLoaded', $stateParams.myArg);
    }
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.10/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/1.0.20/angular-ui-router.js"></script>

<div ng-app="myApp">
  <a ui-sref="child_state({myArg: 'first'})">First link</a>
  <a ui-sref="child_state({myArg: 'second'})">First second</a>
  <div ui-view></div>
</div>
fracz
  • 20,536
  • 18
  • 103
  • 149
1

Is it possible for me to access myArg and have it displayed in the html page parent.html?

That is against the principle of the UI-Router. Parent params can be consumed in children, but not vice versa. How would parent view know about changes WITHOUT re-initializing the controller? You need something like watching.

The true way is to employ Multiple Named Views. Look at this working plunkr.

Amirhossein Mehrvarzi
  • 18,024
  • 7
  • 45
  • 70
0

Yes, this is possible.

  1. Using $stateChangeSuccess:

You can use $stateChangeSuccess to achieve this.

For example:

.state('main.parent', {
  url: '/parent',
  controller: 'ParentController',
  controllerAs: 'vm',
  templateUrl: 'app/parent.html',
  data: {
    title: 'Parent'
  }
})
.state('main.parent.child', {
  url: '/child',
  controller: 'ChildController',
  controllerAs: 'vm',
  templateUrl: 'app/child.html'
})

And in the runblock call it as follows:

$rootScope.$on('$stateChangeSuccess', function (event, toState, fromState) {
  var current = $state.$current;
  if (current.data.hasOwnProperty('title')) {
    $rootScope.title = current.data.title;
  } else if(current.parent && current.parent.data.hasOwnProperty('title')) {
    $rootScope.title = current.parent.data.title;
  } else {
    $rootScope.title = null;
  }
});

Then you can access the $rootScope.title from the child controller since it is globally available.

  1. Using a Factory or Service:

By writing setters and getters you can pass data between controllers. So, you can set the data from the child controller and get the data from the parent controller.

'use strict';
(function () {
    var storeService = function () {
        //Getters and Setters to keep the values in session
        var headInfo = [];
        return {
            setData: function (key, data) {
                headInfo[key] = data;
            },
            getData: function (key) {
                return headInfo[key];
            }
        };
    };
    angular.module('MyApp')
        .factory('StoreService', storeService);
})(angular);

Set data from child controller

StoreService.setData('title', $scope.title)

Get data

StoreService.getData('title');
  1. Using events $emit, $on:

    You can emit the scope value from the child controller and listen for it in the parent scope.

Tessy Thomas
  • 1,475
  • 12
  • 19