Skip to content

resolve value as string? #2047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
allenhwkim opened this issue Jun 18, 2015 · 7 comments
Closed

resolve value as string? #2047

allenhwkim opened this issue Jun 18, 2015 · 7 comments

Comments

@allenhwkim
Copy link

The below question is exact the same as stackoverflow

With ui-router, I add all resolve logic in state function like this;

    //my-ctrl.js
    var MyCtrl = function($scope, customers) {
      $scope.customers = customers;
    }

    //routing.js
    $stateProvider.state('customers.show', {
      url: '/customers/:id',
      template: template,
      controller: 'MyCtrl',
      resolve: {   // <-- I feel this must define as like controller
        customers: function(Customer, $stateParams) {
          return Customer.get($stateParams.id);
        }
      }
    });

However IMO, resolve object must belong to a controller, and it's easy to read and maintain if it is defined within a controller file.

    //my-ctrl.js
    var MyCtrl = function($scope, customers) {
      $scope.customers = customers;
    }
    MyCtrl.resolve = {
      customers: function(Customer, $stateParams) {
        return Customer.get($stateParams.id);
      };
    };

    //routing.js
    $stateProvider.state('customers.show', {
      url: '/customers/:id',
      template: template,
      controller: 'MyCtrl',
      resolve: 'MyCtrl.resolve'   //<--- Error: 'invocables' must be an object.
    });

However, When I define it as MyCtrl.resolve, because of IIFE, I get the following error.

Failed to instantiate module due to: ReferenceError: MyCtrl is not defined

When I define that one as string 'MyCtrl.resolve', I get this

Error: 'invocables' must be an object.

I see that controller is defined as string, so I think it's also possible to provide the value as string by using a decorator or something.

Has anyone done this approach? So that I can keep my routings.js clean and putting relevant info. in a relevant file?

@ProLoser
Copy link
Member

You can do:

$stateProvider.state('customers.show', {
      url: '/customers/:id',
      template: template,
      controller: MyCtrl,
      resolve: MyCtrl.resolve
    });

How you choose to get the MyCtrl reference is up to you. However what you describe requires building new functionality into angular's dependency injection, which is outside the scope of this project. If we choose to pivot our design to be closer to that of ng2 it may be something we do, but it won't be done using the API you described in all likeliness.

@jongunter
Copy link

@allenhwkim I had this same issue and I ended up creating a provider with a method, rather than trying to take IIFEs off my controllers. For example, I was trying to load a document from an API before the state got loaded.

 angular
        .module('myApp.common')
        .provider('DocumentHelper', DocumentHelperProvider);

    function DocumentHelperProvider() {

        // Return a public interface
        return {
            resolveDoc: resolveDoc,
            $get: function () {
                resolveDoc: resolveDoc;
            }
        }

        // Provide document data before loading a document
        function resolveDoc() {
            return {
                Document: ['DocumentService', '$stateParams', function (DocumentService, $stateParams) {
                    // Return a promise from the DocumentService
                    return DocumentService.get({ id: $stateParams.documentId });
                }]

            }
        }


    } // DocumentHelperProvider

@ProLoser
Copy link
Member

ProLoser commented Feb 9, 2016

Honestly, I do not agree with this approach at all now (in the ng1 context) so I'm going to lock this thread.

When you define a state you can (and will) define multiple controllers (for each view) but you can only define one resolve. For this reason alone thinking of resolves as 1-1 with controllers is a bad idea. You may need a few resolves for one controller and more resolves for a second controller and perhaps even more resolves for a controller in a substate.
For this reason, chasing down controller properties to adjust resolves doesn't make sense.

Instead, try not to think of the controller as the important information holder in your app. Controllers should be simple, dumb and cheap, like if you need just one object for the nav view but three objects for the main view, those controllers should be 1 and 3 lines of code respectively.
Instead, consider the state definition itself the important source of truth. The state defines not only layout but also currently loaded data and rules for entry. Keep your state definition plain and you will find its much easier to add resources and structural changes when your app becomes more complex.

@christopherthielen
Copy link
Contributor

Well said proloser.

@jongunter
Copy link

@ProLoser You make a valid point...however..consider this scenario (which I'm currently working on in a project):

  1. You have a template with a bunch of read-only data fields
  2. The fields won't change once they're loaded so you should have one-time binding for performance concerns
  3. If you load the data on controller activate(), you can't take advantage of one-time binding because the data will not be loaded once the controller activates (and thus, all these fields will be blank values and never update).
  4. Since the view won't load until the data has been fetched (the resolve promise is resolved), you can use one-time binding syntax with a route resolve. It should provide a nice performer boost because there will not be hundreds of little watchers.

Am I approaching this issue the wrong way? This seems like it could be a pretty frequent use case.

@ProLoser
Copy link
Member

ProLoser commented Feb 9, 2016

  1. You CAN use one-time binding if the data comes in late. The binding doesn't disconnect until the value becomes anything other than undefined
  2. I don't know about activate(), are we talking about ng2? I was not really addressing that scenario.
  3. I'm totally confused now. If you use resolve, the data comes in before the controller loads, so I don't know why one-time binding is even brought up. However, the issue being discussed has nothing to do with one-time binding or activations, etc. It's merely a choice of where you store your resolve definition. Simply putting your resolves as a property of your controller vs defining a simple pojo inline has no effect on anything, it's merely an organizational decision.

Honestly, if you were to say "Okay, i don't want to load this data over and over" then I'd propose caching it in your services that retrieve the data or moving some of the data being resolved up the state tree to where you feel it'd be more appropriate (which further illustrates my point that you should not tie your resolve definitions so closely to controllers).

Another point that occurs to me is that 1 resolved resource will actually be used in MULTIPLE controllers, so again it doesn't make sense to directly associate resolve definitions with any specific controller.

@jongunter the points you bring up confuse me a bit because I don't really see how they are particularly relevant to this issue. See point 2 if you have performance issues, this discussion was about code organization.

@ProLoser
Copy link
Member

ProLoser commented Feb 9, 2016

@jongunter as a side note, I think your code sample over-complicates things (I hate messing with providers in my app) and I don't see what benefit it provides. I also don't really get what it does and don't think it works the way you think it's working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants