Search code examples
javascriptangularjsng-controller

Deleting angular controller instance


For quick reading, the problem is simplified under "The problem", and for further information keep going down for background and notes before answering. Thank you!

The problem

I want to delete the instance of a controller when refreshing the page or when moving to a different view and then returning to the same view through the navigator (nav.html). In fact, every time view X.html is visited, I want the program to check if X-controller.js exists, and if it does delete it before making a new instance.

How far am I going here, is it a 2 line solution I failed to find online or am I looking at hours of coding to make this work?

Background

My project uses the $routeProvider service, not the the ng-controller directive. Once the app launches there are constantly two views, one on the top where you can navigate back and forth through the controllers "Home - Contact - Support" (logically, nav.html), and one on the bottom which is the "Home" or "Contact" and so on.

I haven't had any problems with this arrangement until the code begun making massive calculations. The same instance of the controller is updated with more data than it should, calculates for previous data that was discarded, and so on. I've read online about deleting the controller but as far as I know it's not that easy.

Notes before answering the question:

  • If the second option of 'hours of coding' is the solution I'm not expecting anyone to do this for me, but references to articles or code for that would be appreciated because I haven't found anything useful on my own.
  • If there is an easier solution that applies only when ng-controller is used and not $routeProvider then it's not an option for me. There are over 20 views and many sections of code which triggers redirection to a different view with a different controller using event listeners. I'm not currently planning on changing $routeProvider to ng-controller.
  • If the solution doesn't actually delete the previous instance, rather clears the $scope and javascript variables then that could work for me as well.
  • I haven't included code because this question is not about a bug or error, if for some reason code snippets of the $routeProvider configuration or one view and controller is needed let me know and I'll include that code with the classified sections replaced with similar dummy code.

Clarification Edit

I'll illustrate with an example. Assume X.html is a view controlled by XCtrl.js. $scope.test is initiated in the beginning $scope.test = 2 of that controller, and once a button in the view is clicked $scope.test becomes 3. Also, the X view displays $scope.test all the time. So I moved to that view, clicked the button, and saw that 3 is displayed on the screen. Then I moved to "Home" through the navigator, then back to "X", and 3 is still displayed. But what I want is 2 to be displayed, and not 3. I want everything to be renewed in that controller.

Solution

Eventually I used a different technique to solve this. All the data saved in the local storage was affecting the $scope variables (there were too many variables to track that I didn't notice this). To solve the issue I cleared the local storage key localStorageService.set('keyUsed', []); once the view controlled by controller X is visited. Assume an init function, so the line of code clearing the local storage was placed in the top of that function.

I'm still marking the correct solution from the answers below for the problem I initially thought I had.


Solution

  • Always have a '.' in your ng-models!

    -- Miško Hevery (father of AngularJS)

    Most likely you have an issue with $scope.test and not with controller itself. If your template x.html refers the value of test as {{test}} (without any prefix) then most probably you are referring the test of the wrong scope. It usually happens due to prototypical chain of scopes that extend one another and fallback to prototype property value. In this case, choose something unique for XCtrl controller and put all your state inside this controller to that namespace. For example, use x as namespace

    $scope.x = {};
    $scope.x.test = 2;
    

    instead of just

    $scope.test = 2;
    

    Then in x.html refer the value as

    {{x.test}}
    

    This is one of the Angular best practices.

    Another best practice, that may solve your issue, is actually not using $scope at all, but use controller instance itself for storing state in junction with controllerAs syntax:

    index.html

    <!DOCTYPE html>
    <html ng-app="app">
    
      <head>
        <script data-require="[email protected]" data-semver="1.5.8" src="https://code.angularjs.org/1.5.8/angular.min.js"></script>
        <script data-require="[email protected]" data-semver="1.5.8" src="https://code.angularjs.org/1.5.8/angular-route.min.js"></script>
        <link rel="stylesheet" href="style.css" />
        <script src="script.js"></script>
      </head>
    
      <body ng-view>
    
      </body>
    
    </html>
    

    script.js

    angular.module('app', ['ngRoute'])
      .config(['$routeProvider', function($routeProvider) {
        $routeProvider
          .when('/x', {
            controller: 'xCtrl',
            controllerAs: 'x',
            templateUrl: 'x.html'
          })
          .when('/y', {
            controller: 'yCtrl',
            controllerAs: 'y',
            templateUrl: 'y.html'
          })
          .otherwise({
            redirectTo: '/x'
          })
      }])
      .controller('xCtrl', [function() {
        var x = this;
        x.test = 2;
        x.doSomething = function() {
          x.test ++;
        };
      }])
      .controller('yCtrl', [function() {
        var y = this;
        y.hello = 'Hello';
      }])
    

    x.html

    <h1>X.html</h1>
    <p>test = {{x.test}}</p>
    <button ng-click="x.doSomething()">+1</button>
    <a href="#/y">Link to Y</a>
    

    y.html

    <h1>Y.html</h1>
    <p>{{y.hello}}</p>
    <a href="#/x">Link to X</a>
    

    Live Demo:

    https://plnkr.co/edit/EpUn94uWMliTaG5mvPxv?p=preview

    Links:

    • To understand the issue with not having a '.' in your model, see this video by Miško Hevery.
    • To better understand controllerAs approach, you can read this post by Todd Motto.