scope and controller instantiation with ui router
Asked Answered
L

3

28

I am confused about when controllers get instantiated. Also, how do controllers gets instantiated when nesting states. I might be confused how scope gets attached to view and controller, that is, if every view gets its own controller and scope or do they share the same scope.

Can someone please explain when controllers get instantiated? Under nested routes do all the views share one controller and scope? What happens when I switch states and go back to a state does another controller get instantiated?

Below are my routes(config file ):

.config (googleAnalyticsCordovaProvider, $stateProvider, $urlRouterProvider, IdleProvider, KeepaliveProvider) ->

   $stateProvider

  .state('app', {
    url: '/app',
    abstract: true,
    templateUrl: 'templates/menu.html',
    controller: 'AppController'
  })

  .state('app.pincode', {
    url: '/pincode',
    views: {
      menuContent: {
        templateUrl: 'templates/pincode-yield.html',
        controller: 'PincodeController'
      }
    }
  })

  .state('app.pincode.create', {
    url: '/create',
    views: {
      pincode: {
        templateUrl: 'templates/pincode-create.html',
        controller: 'PincodeController'
      }
    }
  })

  .state('app.pincode.pincodeLogin', {
    url: '/login',
    views: {
     pincode: {
        templateUrl: 'templates/pincode-login.html',
        controller: 'PincodeController'
      }
    }
  })

  .state('app.pincode.settings', {
    url: '/settings',
    views: {
      pincode: {
        templateUrl: 'templates/settings.html',
        controller: 'PincodeController'
      }
    }
  })
Leaseback answered 17/3, 2015 at 18:30 Comment(0)
H
34

To get even more detailed answers, we can/should observe the source code and check the documentation. Let me try to explain all three questions (and also cite from code and doc).

1. When do controllers get instantiated?

Here we can observe the code of the ui-view directive:

[$ViewDirective.$inject = \['$state', '$injector', '$uiViewScroll', '$interpolate'\];][1]

Controllers are related to views. Those views, which are defined inside of a .state() as the views object:

.state('...', {
  // The view definition
  views : {
    '' : {
      template: ...
      controller: ...
      resolve: ..
    }
  },
  resolve: ...
}

So, whenever is view (the ui-view) filled with settings defined inside of a state view, it acts almost as a standard, but enhanced directive.

1) Template is found,
2) Resolves are resolved
...
x) Controller is instantiated...

View targets (ui-view directives) could use names, and could be filled by different states in the hierarchy.

It could mean, that there could be a content inside of one view (e.g. title), defined by parent as well as replaced by child

// parent
.state('parent', {
  views : {
    '' : {...} // the main parent view, with ui-view="title"
    'title@parent' : { ...} // here we go and fill parent's ui-view="title"
  },
  ...
}

// child
.state('parent.child', {
  views : {
    'title' : { ...} // here we change the parent's target ui-view="title"
  },
  ...
}

The above state definition will (whenever we transition among these two states) do:

  • The $state.go('parent') - the view (template, controller...) defined in 'title@parent' : { ...} will be injected into target ui-view="title" and instantiated as described above

  • The $state.go('parent.child') - almost the same, just the view will be taken from child state/view defintion 'title' : { ...}. That will replace the content of the ui-view="title" and will be instantiated as described above

This will be happening every time we do go from parent to child and from child to parent.

2. Under nested routes do all the views share one controller and scope?

A simple answer is NO, there is no common sharing.

In fact, each controller has its own scope, the one which is created from parent view scope. Firstly the documentation:

What Do Child States Inherit From Parent States?

...

Scope Inheritance by View Hierarchy Only

Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).

It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.

So, whenever is our controller (well the view with template, controller...) injected into parent's target ui-view="..." it gets inherited scope:

newScope = scope.$new();

That in a nutshell means that JS objects (e.g. scope.Model = {}) can be shared among child and parent.

$scope.Model.id = 1; // will refer to the same id in both parent & child

However, basic Javascript types are not passed by reference, and so their values are not automatically synchronised between scopes:

// set in parent
$scope.id = 1;
// in child after inherted still === 1
$scope.id = 2; // now 2 for a child, different value in parent - still === 1

It's worth reading more about prototypical inheritance here:
What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

3. What happens when I switch states and go back to a state - does another controller get instantiated?

It depends.

If the parent sub view (remember ui-view="title" above) is replaced by child view, and then it is re-created (transitioning from child to parent) - such controller wil be re-initialized (discussed above).

But when we speak about the main parent view (usually unnamed), which represents the parent (For example the unnamed view below with controller 'ParentMainCtrl')

.state('parent', {
  views : {
    '' : {  //  // the main parent view
      controller: 'ParentMainCtrl',
    }
    'title@parent'
    'tooltip@parent'
  },

Then we can be sure that such controller is NOT re-instantiated. It lives during the lifetime of all its children, plus a parent's one (no child state selected).

To re-load this view/controller, we have to use an option reload

$state.go(to, params, options)

... options Options object. The options are:

  • ...
  • reload - {boolean=false}, If true will force transition even if the state or params have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd use this when you want to force a reload when everything is the same, including search params.

Hope that helps a bit. For further information, check out these resources:

Highbred answered 18/3, 2015 at 5:52 Comment(0)
S
2

Controllers get instantiated whenever you visit the specific state. For example, while visiting app.pincode.pincodeLogin for the first time one AppController and two PincodeControllers are constructed, each with its own view assuming you got the templates right. Switching to 'app.pincode.settings' would destroy the innermost controller and replace it with a new one, althought the two controllers higher at the hierarchy will not be touched. Scopes follow the standard AngularJS' inheritance pattern, they are not isolated.

You probably would want to remove the controllers in the sub states (and handle the business logic in the parent controller) or have a distinct controller for each state - the same controller for different templates and views is usually a sign of bad design.

Sonnier answered 17/3, 2015 at 18:52 Comment(5)
The two PincodeControllers that get created, is one for pincode and the other for pincode.pincodeLogin? Also, if i modify my code to remove the controller on the stub stages. Would that create 1 pincode controller and one app controller? Also, What about when navigating sub states...do new pincode controller get instantiated?Leaseback
1. yes, 2. yes, 3. yes, in place of the previous substate controllerSonnier
So I made those changes. I added abstract: true to the pincode parent controller and removed the controller in the substates. I also added an alert statement at the top of the controller to see when the controller gets instantiated. What I noticed is that the alert statement only gets fired once at the beginning when the app loads. I would expected to alert every time i switch states. Are my expectations correct or did i mess something up?Leaseback
One last thing, you mentioned that controllers get instantiated the first time you visit that state. What happens when you revisit the state, does a new controller get instantiated again?Leaseback
Yes again :) But only for the part that actually changed. If its a controller of a root state, that is a parent to all other states it will be instantiated just once. Think of ui-router as this lazy guy that will only do the necessary job to fulfil your requirements and doesn't have caching or memoization.Sonnier
W
2

Controllers get instantiated when the corresponding views are loaded for the first time.

For example if you have 3 tabs associated with 3 controllers - then the controller associated with the default view instantiates First. Next when you load the other views the associated controllers also get instantiated.

But interestingly, once a view is loaded in the DOM - it is cached by-default. When a view is navigated away from, its element is left in the DOM, and its scope is disconnected from the $watch cycle. When navigating to a view that is already cached, its scope is then reconnected, and the existing element that was left in the DOM becomes the active view.

Whitman answered 2/4, 2015 at 12:23 Comment(5)
May i know how shall we use the different controllers for different view. Say on click on tab-2 i want to load the tab2Controller only.Flavius
@Flavius Could you please elaborate the question ? BTW - which version of Ionic you are using ?Whitman
Please refer this link [#27768533, In this at last he is using same controller called editController across all nested views but I want to use different controllers say editDetailsController for edit.details and editInfoController for edit.info. May i know how can we achieve this??Flavius
@Flavius : The purpose of such segregation of controllers is not clear to me though. However, here is a working example [plnkr.co/edit/35j5b1Tfl5RTPtBApHCs?p=preview] showing how 2 different controllers used for nested views. Please review. The main idea is to inject the parent scope into the child controllers - so that with scope chain - you can access the parent model.Whitman
Please refer this [#42088149, Just few mins ago i posted this question. Hope it may help you, to help me :)Flavius

© 2022 - 2024 — McMap. All rights reserved.