Skip to content

RFC: Routing to Angular 1.5 .component() #2627

Closed
@christopherthielen

Description

@christopherthielen

I'd like to solicit some feedback from the community about routing to ng1 .component()s.

Backstory

We recently added support for $scope.$resolve to the master and legacy branches. After the next release, you will be able to route to components, and access resolved data using a "component template", similar to ngRoute. Here's a proof of concept: http://plnkr.co/edit/mG9W1q6B4CCHI6QEfKSJ

In angular2, however, we cannot use "component templates", and instead will be routing to a component class. In ui-router-ng2, you will supply the component's class in a state definition, probably something like this:

import {FooComponent} from "./fooComponent.js";
import {UIRouter, StateRegistry} from "ui-router-ng2";

let stateRegistry = uirouter.stateRegistry; // uirouter instance comes from somewhere
stateRegistry.state({
  name: 'foo',
  component: FooComponent, // Reference to the FooComponent class
  url: '/foo/:fooId'
}

I would like both ui-router for ng1 and for ng2 to have a similar look and feel when routing to components. Because of that, I'm considering allowing component: in a state definition for ui-router 1.0 for ng1.

There are a few mechanisms I've been tossing around as options for declaring a state's view should route to a component, and how to provide resolved data to that component. Note that we can't use constructor injection into a .component() to provide resolve data like we would for a normal ui-router template/controller combo, because we can not instantiate and manage the component lifecycle ourselves.


Given this angular 1.5 .component()

app.component('myComponent', {
  templateUrl: 'myComponent.html',
  controller: 'MyController',
  bindings: { input1: '<', input2: '<' }
});

Options

Option 1: Keep it as-is. Use a template: and access data using $resolve.
$stateProvider.state('foo', {
  template: '<my-component input1="$resolve.foo" input2="$resolve.bar"></my-component>',
  url: '/foo/:fooId/:barId',
  resolve: {
    foo: ($stateParams, FooService) => FooService.get($stateParams.fooId),
    bar: ($stateParams, BarService) => BarService.get($stateParams.barId)
  } 
});
Option 2: Route to the component by name.

Require each component bindings to match a resolve name

$stateProvider.state('foo', {
  component: 'myComponent',
  url: '/foo/:fooId/:barId',
  resolve: {
    // input1 and input2 are same names as myComponent `bindings`
    input1: ($stateParams, FooService) => FooService.get($stateParams.fooId),
    input2: ($stateParams, BarService) => BarService.get($stateParams.barId)
  } 
});
Option 3: Route to the component by name + bindings mapping string

Allow resolve names to be mapped to component bindings using a DSL similar to ui-sref
The use case is that resolves may come from parent states, so may need to be mapped to the component bindings

$stateProvider.state('foo', {
  component: 'myComponent({ input1: foo, input2: bar })',
  url: '/foo/:fooId/:barId',
  resolve: {
    foo: ($stateParams, FooService) => FooService.get($stateParams.fooId),
    bar: ($stateParams, BarService) => BarService.get($stateParams.barId)
  } 
});
Option 4: Route to the component by name + bindings mapping object

Allow resolve names to be mapped to component bindings using an object
Again, the use case is that resolves may come from parent states, so may need to be mapped to the component bindings

$stateProvider.state('foo', {
  component: 'myComponent',
  bindings: { input1: "foo", input2: "bar" },
  url: '/foo/:fooId/:barId',
  resolve: {
    foo: ($stateParams, FooService) => FooService.get($stateParams.fooId),
    bar: ($stateParams, BarService) => BarService.get($stateParams.barId)
  } 
});
Option 5: Route to the component by name; Supply resolves via ui-view controller

For this option, the component has to be aware that it's inside of a ui-view. It requires the ui-view controller and accesses the resolves from there. The bindings object is not used.

app.component('myComponent', {
  templateUrl: 'myComponent.html',
  controller: function() {
    this.$onInit = function() {
      this.input1 = this.uiView.$resolve.foo;
      this.input2 = this.uiView.$resolve.bar;
    }
  },
  require: { uiView: '^uiView' }
});

$stateProvider.state('foo', {
  component: 'myComponent',
  url: '/foo/:fooId/:barId',
  resolve: {
    foo: ($stateParams, FooService) => FooService.get($stateParams.fooId),
    bar: ($stateParams, BarService) => BarService.get($stateParams.barId)
  } 
});

Option 6: Your option

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions