-
Notifications
You must be signed in to change notification settings - Fork 3k
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
RFC: Routing to Angular 1.5 .component() #2627
Comments
I like option 2. It aligns more with how the components are dynamically loaded in Angular 2. How would these resolved values get set for the state component? |
Option 1This is what i'd go for. The UI router API is pretty comprehensive already so I wouldn't feel comfortable adding more things. Option 2I feel like there would be too much magic going on, especially when someone else picks up the code and doesn't know that resolves are autobound to the component's bindings Option 3Not a fan of micro DSLs unless they're unavoidable Option 4This looks to me like a better version of Option 2 but I still prefer Option 1 Option 5Least favourable option in my opinion as it ties a component to UI router which renders it more or less unusable in other contexts which is the idea of a component |
I'm mostly leaning towards options 2 and 4. My thought would be to default to the syntax of option 2 but provide the bindings from option 4 as a mapping syntax for when it might be necessary. |
Option 2, and it is (should) be compatible (extensible) with Option 4. |
@brandonroberts much like the plunker above (see: "routeToComponent.js")
|
@luciano-spinn3r I'm having trouble understanding all of your points, but it sounds like you are advocating for a drastic architecture change, along the lines of the component router. That's not on the table here; ui-router will continue to use the state tree abstraction to define the application structure and data retrieval/lifecycle. Components are applied as views. Feel free to correct me if I missed some nuance of your suggestion. However, I don't want to derail the initial RFC. |
I was laking this functionality, as I'm looking forward to achieve a full angular app architecture based on routable components (just to test it vs a classical one) and balance the benefits and disadvantages of working with this flow. I'm my opinion, option 2 as default with optional 4 (more flexible, would override default - good point on parent resolves!) should be the way. Thanks for all and keep up the good work! |
Option 2 looks good to me |
I think option 4 looks the best, with sensible default (bindings can be omitted with a default to an empty object). |
Do you mean you prefer by default, that no automatic bindings would occur like option 2? |
Yup |
option 2 as default with optional 4 |
1 should be supported, and 4 would be nice as an addition. |
My vote goes to option 2. |
I think 1 should be supported just to be related to the ngRoute syntax. However, I like 2 as the default. It makes sense that you can provide the bindings through a resolve, though it is possible that a binding might not be required, so it should not get upset by not providing a binding and default to |
Done... options 1, 2, and 4 are supported. This works for:
This code works by decorating states that have |
Nice work!. All seems to be fine fine with Angular 1.5 except it seems |
@samithaf did you add the angular module dependency to "ui.router.events" as well? |
hey @christopherthielen thanks for getting back and the hint. Wonder module name is
|
Yes! Whoops.
|
Amazing work, thanks :D |
How would one implement lazyloading with the native 1.5 components? |
This is awesome, thanks a lot! Question: is there a way to use options 2 or 4 to route to a ng1 component that also exposes some outputs ( For example: module.component(`foo', {
template: `<a href ng-click="$ctrl.output()">click me {{ $ctrl.input }}</a>`,
controller: function() {},
bindings: {
input: `<`,
output: `&`
}
})
.config($stateProvider => {
$stateProvider.state(`foo`, {
url: `/foo`,
component: `foo`,
resolve: {
input: () => `Francesco`,
output: () => () => alert(`Clicked!`)
}
});
}); |
Lazy load componentsHere's an example of a lazy loading hook: http://plnkr.co/edit/3kvrVY?p=preview |
@christopherthielen |
I have a question about the component -syntax. I have a parent component, and N-number of child components, which require data resolved by parent component:
And in the state declarations I want to use the nice new component syntax:
Now the problem is that parentComponent is lacking
This is what I am using now (extend extra div in component declaration):
Is there any way to append |
@fpipita Interesting idea... can you provide me with a concrete example of how you might use the & binding? I'm considering allowing any type of component binding, but I'm struggling with a compelling or meaningful use case for "&" binding from a resolve. Would the callback be defined on a parent state, or the same state? Would it interact with other resolves? Respond to UI events? I'm wondering if the intention is to get some "smart component" functionality? Would it be simpler to introduce a "smart component" that wraps your other component, and route to that instead? |
@xeii I don't quite understand all the pieces you've shown What else does your component do? Why do you bind the resolve data to the component, if it has no template or controller? It might be easier to just use a standard state definition with a template: In general, I feel like this is a pretty different than how most people will use route-to-component. I suggest explicitly adding the http://plnkr.co/edit/xZhaQd?p=preview This $stateProvider.decorator("views", (state, parentFn) => {
// Call the parent views: builder.
// This thing takes the state, converts the its views: declaration
// into the internal `views` representation object
let viewsObj = parentFn(state);
angular.forEach(viewsObj, (view, key) => {
if (view.component) {
// it has a component: declaration... let's
// decorate the results of the real template provider
// First, save a copy of the stock ui-router templateProvider
let realTemplateProvider = view.templateProvider
// Then, override the view's templateProvider with our own
view.templateProvider = [ "$injector", function($injector) {
// Invoke the original templateProvider to get the template
let realTemplate = $injector.invoke(realTemplateProvider);
console.log('original template: ', realTemplate);
// Use string replacement to add the ui-view
let newTemplate = realTemplate.replace("><", " ui-view><");
console.log('replaced template: ', newTemplate);
return newTemplate;
}];
}
});
return viewsObj;
}) |
@christopherthielen What I need is the resolved data by the parent component to be accessed in child components, thus this (in child component declaration):
This is the best aproach I could think of using components. And I really liked the way I could keep the state declarations very clean. The 'parent' here does not need to be a component, like you said it does not have any template. |
I tried to bring this up for discussion once, but it hasn't been well Luciano Greiner On Mon, May 9, 2016 at 4:00 AM, Antti Väyrynen [email protected]
|
It seems that your parent component doesn't do anything besides encapsulate the resolve data? A child "route-to-component" can directly bind to resolves from the parent states. .component('childComponent', {
controller: ChildController,
template: ChildTemplate,
bindings: {
input1: '<',
input2: '<'
}
}) .state('parent', {
abstract: true,
template: '<ui-view></ui-view>'
},
resolve: {
input1: () => data1
input2: () => data2
}
.state('parent.child', {
component: 'childComponent'
}) |
@lucianogreiner I'm listening.
I don't understand your point though, can you try again? |
This works because ui-router uses $scope behind the scenes? Anyway, would be nice to have that example in the docs. And thanks for the responses! |
@christopherthielen thanks for your reply.
Sure, here you go: angular
.module(`app`, [`ui.router`])
.factory(`ItemService`, () => {
const itemList = [
{id: 1, name: `computer`},
{id: 2, name: `mouse`},
{id: 3, name: `keyboard`}
];
return {
getAll: () => [...itemList],
getById: id => itemList.find(i => i.id === id)
};
})
.component(`item-list`, {
template: `
<ul>
<li ng-repeat="item in $ctrl.itemList"
ng-click="$ctrl.onOpenItem({$event: {item: item}})">
{{ item.name }}
</li>
</ul>`,
controller: function() {
},
bindings: {
itemList: `<`,
onOpenItem: `&`
}
})
.component(`item`, {
template: `<h1>{{ $ctrl.item.name }}</h1>`,
controller: function() {
},
bindings: {
item: `<`
}
})
.config($stateProvider => {
$stateProvider
// here the & binding would come handy because the template
// is only needed to inject the callback.
// A thing worth noting is that there should be a mechanism
// that allows to specify how to pass the properties exposed by the component's
// output on the template to the callback ($event in this case)
.state(`itemList`, {
url: ``,
template: `
<item-list itemList="$resolve.itemList"
on-open-item="$resolve.onOpenItem($event)">
</item-list>`,
resolve: {
itemList: ItemService => ItemService.getAll(),
onOpenItem: $state => $event => $state.go(`item`, {
itemId: $event.item.id
})
}
})
.state(`item`, {
url: `/:itemId`,
component: `item`,
resolve: {
item: ($stateParams, ItemService) => ItemService.getById(+$stateParams.itemId)
}
});
});
From my point of view, the callback should work just like any other resolve, the only "unusual" thing here is that the resolve returns a function. Interaction with other resolves would be possible through the closure.
Wrapping the component within a "smart component" is definitely an option, thanks for pointing me to it. If I'll find any other meaningful and compelling use case for the |
@fpipita After playing with Here is my reasoning...
|
Correct me if I am wrong option 4 syntax needed to updated as follows.
|
Do we have any working examples of using ui-router and components with all or any options discussed above? Based on my search, I just get this issue and examples of using template: |
@siddharth-pandey I'm successfully using this config (with version 1.0.0-beta.1):
|
@fasfsfgs Thanks for sharing your code. I'm using ui-router's version v0.2.18 and have tried to render similar code. Is this way of specifying component name supported in v0.2.18? If not, which one should I use with Angular version 1.5 to use components? |
@siddharth-pandey As far as I know only ui router 1.0 (which is not released yet) deals directly with components. So you'd have to use a beta version to get this functionality (like I have). If you stick with released versions, your solution with templates is the one to go. |
Not working resolve and component (method 2) using 1.0 beta1... The resolved parameters are undefined in the controller component edit: Does not work with named ui-views fiddle: http://jsfiddle.net/789Ks/96/ |
@Luddinus did you find a workaround for that? Same problem here.. |
on v0.2.18, you can use option1: with template. works fine for now if you don't want to upgrade to the beta. |
Also note that there is a polyfill for 0.2.18 based on my original route-to-component plunker: https://github.com/softsimon/ui-router-route-to-components |
The must have update dependencie is the `angular-ui-router` simply because it now support the components as template as mention here angular-ui/ui-router#2627 the other ones, just to have everything updated
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 themaster
andlegacy
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/mG9W1q6B4CCHI6QEfKSJIn 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:
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()
Options
Option 1: Keep it as-is. Use a
template:
and access data using$resolve
.Option 2: Route to the component by name.
Require each component
bindings
to match a resolve nameOption 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-srefThe use case is that resolves may come from parent states, so may need to be mapped to the component
bindings
Option 4: Route to the component by name + bindings mapping object
Allow resolve names to be mapped to component
bindings
using an objectAgain, the use case is that resolves may come from parent states, so may need to be mapped to the component
bindings
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
require
s the ui-view controller and accesses the resolves from there. Thebindings
object is not used.Option 6: Your option
The text was updated successfully, but these errors were encountered: