Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destroy scope before change path in ui-router

I use UI-Router routing. When I change path from a state to another and going back to same state, I see that old $scope in state is there (with it's properties).

I want to destroy that $scope before state changes, So when I come back to state for second time, there will be a clean new scope. I tried to access scope in this event:

$rootScope.$on('$stateChangeStart', 
   function(event, toState, toParams, fromState, fromParams) {
     // fromState.$scope.$destroy();
});

But there isn't any reference to $scope. Can I access scope before change state in angular UI-Router?

like image 928
Morteza Ziyae Avatar asked Feb 11 '23 02:02

Morteza Ziyae


2 Answers

I would say, that what you experience is a bit different than you described, or you thought what is happening. Please, check for example this:

  • How do I share $scope data between states in angularjs ui-router?
  • scope and controller instantiation with ui router

In general, once the state change is done (not rejected), the old $scope is for sure destroyed. If we navigate then back, new $scope is created for us. But this $scope is created this way:

The source code of theviewDirective.js

    function updateView(firstTime) {
      var newScope,
          name            = getUiViewName(scope, attrs, $element, $interpolate),
          previousLocals  = name && $state.$current && $state.$current.locals[name];

      if (!firstTime && previousLocals === latestLocals) return; // nothing to do
      // HERE
      newScope = scope.$new();
      ...

The construct: scope.$new(); is a key to understanding. This in fact means, that we use prototypical inheritance

  • What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

And that in a nutshell could be described:

we are provided with a $scope which has cloned all the properties from its parent.

So if parent contains some reference (has '.' in the path) like this

// parent scope
$scope.Model = {
  ...
};

And any child state will change that like this

$scope.Model.name = "User";

That value will be stored in parent state $scope and available again ... for any next child of this state.

NOTE: the same viewDirective.js but elswhere could be used to demonstrate the fact - $scope is destroyed if we leave the state:

    function cleanupLastView() {
      if (previousEl) {
        previousEl.remove();
        previousEl = null;
      }

      if (currentScope) {
        currentScope.$destroy();
        currentScope = null;
      }
      ...

EXTEND

I created a working example here, with these two states:

.controller('ParentCtrl', ['$scope', function ($scope) { 
  $scope.Model = {
    SharedName: "This is shared name",
  }
  $scope.NotSharedName = $scope.NotSharedName 
                      || "This name is cloned, but then lives its own way";
}])
.controller('ChildCtrl', ['$scope', function ($scope) {}])

And these two ways how to change values (all will follow the logic described above):

<p>this will be shared among all children and parent
  <input ng-model="Model.SharedName" />
</p>
<p>this will always copied from parent, live then in each child
  <input ng-model="NotSharedName" />
</p>

Check it here

like image 59
Radim Köhler Avatar answered Feb 13 '23 01:02

Radim Köhler


I was going to make an add-on comment on Radim Köhler's post but since I don't have enough rep points, I'll just add an answer here that the only solution I've used so far to circumvent this is to use the "controller as" approach and avoid adding methods/properties on the controller scope that I don't want conflicting due to prototypal behavior.

like image 41
ako977 Avatar answered Feb 13 '23 02:02

ako977