davej / angular-classy

Cleaner class-based controllers with Angular 1
http://davej.github.io/angular-classy/
813 stars 27 forks source link

Support resolvers #5

Closed dcramer closed 8 years ago

dcramer commented 10 years ago

Not sure if this is reasonable or not, but we bundle our controllers (using ui-router) into a state, which also lets us easily encapsulate resolvers.

The way we've been doing it resolvers are purely "load this before rendering" (i.e. make API request).

davej commented 10 years ago

Do you have a code example? I believe that standard resolvers using either ng-router or ui-router should just work. Perhaps I'm misunderstanding what you're trying to do though.

dcramer commented 10 years ago

Sorry what I mean is it'd be nice if I could bundle them inside of the controller somehow ;)

Here's the pattern we adopted: https://github.com/dropbox/changes/blob/master/static/js/states/planList.js

davej commented 10 years ago

A Classy controller returns itself, so you could do this:

define(['app'], function(app) {
  'use strict';

  return {
    parent: 'layout',
    url: '/plans/',
    templateUrl: 'partials/plan-list.html',
    controller: app.cC({
      inject: ['$scope', 'planList', 'Collection'],
      init: function() {
        this.$.plans = new this.Collection(this.$, this.planList.data);
      }
    }),
    resolve: {
      planList: function($http) {
        return $http.get('/api/0/plans/');
      }
    }
  };
});

Or are you looking to move your route information inside of the Classy controller?

edit: cC is just a shortcut for classy.controller, you can use either.

dcramer commented 10 years ago

I guess what I'm proposing is something-like-resolvers should be part of a controller/class API.

Realistically they're used for initialization a lot of time, and the dirty work you have to do to hide the controller/show it (without resolvers) is just gross.

davej commented 10 years ago

Ah, so this would be a separate resolver to the one the router provides (and would happen after the router's resolver if the router had a resolve defined)? It's not something that I've thought about to be honest. It's an interesting proposal, I'll have a think about it and get back to you tomorrow after some sleep (I'm GMT).

checketts commented 10 years ago

This is exactly what I'm looking for as well. Resolves are promises that when completed are injected into the controller. I've wanted to keep them in the controller for a long while. So it would match with either the inject section, or something else.

davej commented 10 years ago

Does anybody want to propose an API for this? Should the resolvers re-use the class-wide injected dependencies or should each resolver inject it's own dependencies?

davemo commented 10 years ago

I think the default would be to use the classes inject property when referencing dependencies, with potential for an optional way of declaring that a resolver should derive its dependencies independently.

As for an API, I think something like this perhaps?

app.classy.controller
  name: 'PlanController'
  inject: ['$scope', 'planList']
  resolve:
    plans: -> @planList

Under the hood there's a few things going on here:

  1. planList would have to be either an angular service with a get method, or just a pure function that encapsulated $http.get('/api/0/plans/'); (in either case, the return value would be a promise).
  2. classy would have to resolve the promise, then assign the return value (in this case, the .data) automatically and place it on scope as the value of the key in the resolve object (ie: resolve.plans would become $http.get('/api/0/plans/').data)

The key pieces here are auto resolving the promise and attaching the result to the scope.

davemo commented 10 years ago

Also, given that angular works reasonably well with promises referenced inside templates and will automatically render values once promises are resolved, classy could do the simple thing and just invoke the appropriate resolve key and map the promise onto the $scope

dmackerman commented 10 years ago

Why couldn't the passed resolve just be passed into the inject config, similar to how you would inject it into a regular Angular controller?

inject: ['$scope', '$rootScope', '$kinvey', '$state', '$modal', '$timeout', 'User'
init: function() {
  this.user = User;
}

Why do we need another level of abstraction?

dcramer commented 10 years ago

FYI a lot of the time I dont actually want to bind my resolver to the scope. I generally manipulate it/use it to populate something else.

I think it would be a mistake to assume auto binding resolvers to the scope is a common idiom (it might be, but IMO its an incorrect one)

dmackerman commented 10 years ago

@dcramer: Fair enough. :smile:

I'm using the resolved values directly in some of my controllers, mainly just to prevent route change before data is ready. Other times I'm using a service where the classy API works just great.

davej commented 10 years ago

Ok, I think that the most likely outcome in the short-term is that there will be an API available for extending Classy (see #6, it would be good to have more peoples feedback on what this API should look like). This will allow resolvers to be implemented as a Classy plugin.

If there is an obviously favoured plugin for doing resolvers with Classy and it is used by enough people then it can be merged into the core of Classy.

demetriusnunes commented 10 years ago

@dcramer I really liked your approach to declaring controllers within a state. Did you come up with that convention or is that a common Angular UI Route practice?

dcramer commented 10 years ago

@demetriusnunes I came up with it. I'm not a JS person so I just structure things in a logical way. One of the reasons I chose Angular of Ember in a different project was because it gave me these choices :)

davej commented 10 years ago

It should now be possible to create a Classy plugin that supports resolvers. Just make sure your using the version of Classy from the develop branch (not master).

Take a look here to see how to write a Classy plugin: coffeescript, javascript

When you've created a plugin then you can add the plugin to your app's module definition:

var app = angular.module('app', ['classy', 'classy-yourPlugin']);

Classy itself is now modular and written as plugins, so you could also take a look at the source for guidance on how to write a plugin: https://github.com/davej/angular-classy/blob/develop/angular-classy.coffee

Anybody want to have a go creating a resolve plugin? I'm here to help if anything isn't clear (or if the plugin doesn't expose enough functionality to write a resolve plugin properly).

chrisnicola commented 10 years ago

I was just looking for something like this and realizing AngularJS didn't support it I immediately thought of classy. Support for resolvers as a property on the class would be absolutely awesome. This is exactly where resolvers should go the vast majority of the time.

davej commented 8 years ago

Closing this as Classy won't support it in core. If anybody wants help creating a plugin then just @ me.