Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

$RouteProvider should activate controller, even when no templateUrl or template is passed #1838

Closed

Description

@lukasolson

Sometimes, you don't want to have a template when changing routes. You just want to activate the controller. Currently, it's not possible to do this in Angular.

Activity

  1. leeola commented on Jan 19, 2013

    @leeola

    👍

    Is there a commonly accepted pattern for solving this perhaps? Something akin to writing your own router?

  2. pkozlowski-opensource commented on Jan 19, 2013

    @pkozlowski-opensource
    Member

    @lukasolson This sounds really strange to me. Could you elaborate on your use-case?

    The thing is that the role of a controller is to set-up a scope for a partial (coming from a template or templateUrl) so data and behavior can be available in a partial. If there is no view there is not much need to setup a scope (there is no one to take advantage of it).

  3. lukasolson commented on Jan 19, 2013

    @lukasolson
    Author

    See http://stackoverflow.com/questions/14411740/how-to-route-without-a-templateurl/

    My use case is that I have a route that makes a request to the server and routes to one of two possible routes based on the response. Is there a better way to do this?

  4. abh commented on Jan 19, 2013

    @abh

    Why not just add a small "Loading..." (or empty!) template snippet to the intermediary controller?

  5. leeola commented on Jan 19, 2013

    @leeola

    Why not just add a small "Loading..." (or empty!) template snippet to the intermediary controller?

    But that's changing the view for no reason. The concept is to be able to route something that has no view.

    This is mostly just an event (like a click-logout event), but the importance of this is that it is being routed, thus supporting deep linking for the /logout action.

    I'm not saying what you suggest will not work, it's just that what you suggest is more of a hack. This specific route has no view, and giving it one is a workaround to a problem.

  6. lrlopez commented on Jan 19, 2013

    @lrlopez
    Contributor

    You could add a listener to the $routeChangeSuccess event (have a look into http://docs.angularjs.org/api/ng.$route). Inside the event handler you should be able to do your magic (i.e. querying the server and changing the path when the promise is returned) if the route matches.

  7. petebacondarwin commented on Jan 20, 2013

    @petebacondarwin
    Contributor

    Have you tried putting the code in a resolve handler for the route?

    On 19 January 2013 20:30, Luis Ramón López notifications@github.com wrote:

    You could add a listener to $routeChangeSuccess events (have a look into
    http://docs.angularjs.org/api/ng.$route). Inside the event handle you
    could (i.e. querying the server and changing the path when the promise is
    returned) if the route matches.

    —
    Reply to this email directly or view it on GitHubhttps://github.com//issues/1838#issuecomment-12460636.

  8. lukasolson commented on Jan 21, 2013

    @lukasolson
    Author

    But where would I put the listener on $routeChangeSuccess or the resolve handler? In a controller?

  9. petebacondarwin commented on Jan 21, 2013

    @petebacondarwin
    Contributor

    Inside the resolve handler you could change the location if necessary.
    These handlers always get run before the template is instantiated.

    On 21 January 2013 15:52, Lukas Olson notifications@github.com wrote:

    But where would I put the listener on $routeChangeSuccess or the resolve
    handler? In a controller?

    —
    Reply to this email directly or view it on GitHubhttps://github.com//issues/1838#issuecomment-12503461.

  10. lukasolson commented on Jan 21, 2013

    @lukasolson
    Author

    Ah, wasn't aware of resolve handlers. I guess you could put it there. Something like

    .when("/", {resolve: {redirect: RedirectController}})
    

    where RedirectController is the function that actually redirects??

  11. petebacondarwin commented on Jan 21, 2013

    @petebacondarwin
    Contributor

    Yes something like that but I would not call it a Controller! It will be
    more of a service.

    .when("/", {resolve: {redirect: redirectService}})

    On 21 January 2013 18:21, Lukas Olson notifications@github.com wrote:

    Ah, wasn't aware of resolve handlers. I guess you could put it there.
    Something like

    .when("/", {resolve: {redirect: RedirectController}})

    ??

    —
    Reply to this email directly or view it on GitHubhttps://github.com//issues/1838#issuecomment-12510495.

  12. petebacondarwin commented on Jan 21, 2013

    @petebacondarwin
    Contributor

    If you are happy with this then we should close this issue. Let me know if you think it should be reopened for any reason.

  13. lukasolson commented on Jan 21, 2013

    @lukasolson
    Author

    Sounds good. Thanks for all your help.

  14. leeola commented on Feb 19, 2013

    @leeola

    @petebacondarwin @lukasolson

    I'm noticing a problem with this method. It seems when you use the resolver as a method to trigger logic (such as a redirect or logout handler), it can only occur once.

    If my understanding is right, this is because the service is being loaded so that it can be injected into the controller, but it is only ever loaded once.

    This seems like a rather large flaw in this usage.. unless i am doing something wrong. Thoughts?

  15. lukasolson commented on Feb 21, 2013

    @lukasolson
    Author

    Could you post kind of a code snippet of how you're doing it, @leeolayvar?

  16. ThomasDeutsch commented on Feb 23, 2013

    @ThomasDeutsch

    My call: http://localhost:9000/#/?editMode=1

    as we all know, this is working:

    $routeProvider
          .when('/', {
              controller: function($routeParams, $location) {
                if ($routeParams.editMode == '1') {
                    $location.path('edit'); <-- change to http://localhost:9000/#/edit
                }              
              },
              templateUrl: 'views/nothing.html'
          })
    

    But this is not:

          $routeProvider
          .when('/', {
              resolve: {
                  redirect: function ($routeParams, $location) {
                      console.log($routeParams);  <-- this will return Object{}
                      if ($routeParams.editMode == '1') {
                          $location.path('edit'); <-- change to http://localhost:9000/#/edit
                      }
                  }
                }
              }
          })
    

    It seems that in the redirect function, the $routeParams get not injected properly.

    And then i found out that i have to use $route instead of $routeParams
    oh, i get it... i need to do this:

          $routeProvider
          .when('/', {
              resolve: {
                  redirect: function ($route, $location) {
                      if $route.current.params.editMode == '1') {
                          $location.path('edit');    <-- change to http://localhost:9000/#/edit
                      }
                  }
                }
              }
          })
    

    And this my friends works very well.

  17. leeola commented on Feb 23, 2013

    @leeola

    @lukasolson

    Well i spent forever trying (and ultimate failing) to get a working example of this on Plunkr/Fiddle (hell, even the ngView Doc Example is broken lol), but i played more with my own code and figured it out!

    In my code that was not working, i had roughly this:

    $routeProvider.when('/foo', {
      resolve: { foo: 'fooService' }
    })
    

    and later in my code i defined the service, which was roughly this..

    app.factory('fooService', [$http, function ($http) { //do stuff }]);
    

    Well it seems this is the incorrect way to do it? If i ran the resolve like this, my //do stuff code was only executed once. All subsequent url resolves did nothing.

    Why did i do it this way? Well, i was looking to keep logic code out of the routing code.

    In the end, the solution seems to be to define the function in the route call itself. So, like this:

    $routeProvider.when('/foo', {
      resolve: {
        foo: function ($http) { //do stuff }
      }
    })
    

    I am however a little nervous about injecting $http in the resolve handler like that. Is there a way to inject that in a minify-safe way?

  18. JustinTArthur commented on Apr 19, 2013

    @JustinTArthur

    After seeing these proposed solutions, the original poster's suggestion of allowing template-free controllers still looks cleaner to me. Conditional routing is a common necessity and yet almost no tutorial has dared to share how this has to be done in AngularJS right now.

    The argument that a controller's role is to set up scope is a good one, but $scope is an injected dependency. If a controller doesn't need to set up scope, then in the DI frame of mind, we simply don't inject that dependency, right?

  19. jlmakes commented on Oct 1, 2013

    @jlmakes

    I would welcome support for this; I too tried to implement a /logout route that executes it's controller with no template…

  20. nerdburn commented on Oct 11, 2013

    @nerdburn

    I'm dealing with this same issue and agree that routes that can point to controllers, without the requirement of a templateUrl would be beneficial.

  21. buunguyen commented on Oct 11, 2013

    @buunguyen
    Contributor

    @julianlloyd I dealt with logout before, this is what I did to make it work w/o a controller or template.

     routeProvider.when('/logout', {
       resolve: {
         logout: function($q, $location, User) {
           var deferred = $q;
           User.logout(
             function success() {
               deferred.reject();
               $location.path('/login');
             },
             function error(error) {
               deferred.reject(error);
               $location.path('/');
             }
           );
           return deferred.promise;
         }
       }
     });
    
  22. jlmakes commented on Oct 25, 2013

    @jlmakes

    @buunguyen Awesome, thanks for the idea; here’s my implementation:

     .when('/logout', {
            resolve: {
                logout: function ($q, $location, $http) {
                    var deferred = $q.defer();
                    $http.post('/logout')
                        .success(function (data, status, headers, config) {
                            deferred.reject();
                            $location.url('/login');
                        });
                    return deferred.promise;
                }
           }
    })

    With Express handling POST requests to /logout as such:

    app.post('/logout', function (req, res) {
        req.session.destroy(function (err) {
            if (!err) {
                res.send(200);
            }
        });
    });
  23. andrew-aladev commented on Feb 3, 2014

    @andrew-aladev

    Sometimes, you don't want to have a template when changing routes. You just want to activate the controller. Currently, it's not possible to do this in Angular.

    What do you meen "sometimes"? I want to do this always. Angular is written by wonderful, charming people. This is not surprising in company, that is fully populated by magestic elves.

    Is there a commonly accepted pattern for solving this perhaps

    oh lol. pattern driven development.

    I dont know why people has tolerance about angulajs routing.

    ng-view data will be replaced by provided template (url or string)

    I can say that this is the most crappy abstraction that can be found. It's author should be adored and showered with flowers.

  24. andrew-aladev commented on Feb 3, 2014

    @andrew-aladev

    I have understand why people has tolerance about angulajs routing. Someone messes comments.

  25. caitp commented on Feb 3, 2014

    @caitp
    Contributor

    @andrew-aladev please don't make statements like "Its author should be killed and thrown out of the window." in the future, okay? That is not how things are done. The framework is open to criticism, but there is no place for mean-spiritedness on the bug tracker.

  26. cha0s commented on Mar 2, 2014

    @cha0s

    Yeah this should totally be a thing.

    Also guys above, I just wanted to note that you should call deferred.resolve() instead of deferred.reject() if there is no error.

  27. btoone commented on Jul 2, 2015

    @btoone

    @leeola Thanks for posting about how using a service doesn't work. Saved me tons!

  28. petebacondarwin commented on Jul 2, 2015

    @petebacondarwin
    Contributor

    @cha0s - I think they are calling reject because they want the route change to fail. But since they are changing the location anyway, I don't think this would make much difference...

    @leeola - the reason that your version providing the name of a service to the resolve only worked once is that you are asking the injector to provide a service, which will contain the promise to resolve. Since all services are singletons in Angular, the injector will create this promise on first request but then return the same promise every time afterward. The way to do this better is to define a service that returns a function that will run the request and return a new promise every time it is called:

    app.factory('fooService', [$http, function ($http) {
      return function() {
        return $http(...);
      };
    }]);

    Then in your resolve you can do this:

    $routeProvider.when('/foo', {
      resolve: { foo: ['fooService', function(fooService) { fooService(); } } // note that we are actually calling the service in the resolve
    });

    which is minification safe.

    @jlmakes and @buunguyen - it is worth noting that if you have a promise already, as in the result of calling $http you do not need to provide your own deferred:

     .when('/logout', {
            resolve: {
                logout: function ($location, $http) {
                    return $http.post('/logout').then(function(response) {
                        $location.url('/login');
                        throw '';  // effectively rejects the resolve (but I don't think this is needed)
                    });
                }
           }
    })
  29. Artur93gev commented on Mar 21, 2017

    @Artur93gev

    I have the same issue, but I'm lazy-loading modules using route resolve.
    So I made a service that is helping router to activate the module if the router can't

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions