Skip to content

two active states under one url #339

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
fxck opened this issue Aug 20, 2013 · 19 comments
Closed

two active states under one url #339

fxck opened this issue Aug 20, 2013 · 19 comments

Comments

@fxck
Copy link

fxck commented Aug 20, 2013

Is this possible?

state('root', {
  abstract: true
})
  .state('root.one', {
    url: '/something?foo'
  })
  .state('root.two', {
    url: '/something?bar'
  })

I could put both in the same state and use views, but then a change in parameter on root.one would also re-render root.two
#84 #160 are probably somewhat related issue..

@timkindberg
Copy link
Contributor

What are you hoping to achieve by doing that? How are you expecting it
to function?

@fxck
Copy link
Author

fxck commented Aug 20, 2013

I'm trying to achieve exactly what I described..

Let's say root.one is component with data affected by param foo and root.two is another component with data affected by param bar.. if both components were in the same state, changing either foo, or bar would trigger transition, re-rendering(and loading data) for both root.one and root.two.. Which is kinda unnecessary, since if I changed foo, it'd load bar which was unchanged thus with the completely same data I already have loaded.

Err.. is it any clearer?

@timkindberg
Copy link
Contributor

Well its a bit confusing still what you are trying to avoid.

Let's say root.one is component with data affected by param foo and root.two is another component with data affected by param bar.

This part makes sense. I think your code above is valid, you'd have to try it out to confirm though. Basically, root.one will be active when the url is '/something?foo=val' and then root.two will be active when the url is '/something?bar=val'. If that's all you are asking then YES I think you are good.

if both components were in the same state, changing either foo, or bar would trigger transition, re-rendering(and loading data) for both root.one and root.two.

It's your use of the word "components" that makes this unclear. That word has so many meanings in ui-router world. Are you talking about templates?

Also do you realize that by splitting the "components" out into two different states, that only one will be visible at a time? Whereas if they were in the same state, you could potentially see both on screen simultaneously?

These are the questions that cause me to be confused.

@fxck
Copy link
Author

fxck commented Aug 20, 2013

Component as in some sort of element.. let's say root.one is a carousel with pictures of cats and root.two is a carousel with a picture of dogs. They are both visible at the same time one the same page with url /carousel, and clicking on left and rights arrows on that would change catsPage = catsPage +1 and dogsPage = dogsPage +1 respectively.

Now what would happen after clicking on one of the arrows, if I had both carousels in one state with url: '?catsPage&dogsPage' is, that it would re-render and load data for both carousels. But I would want to re-render and load data only for cats carousel.

Better a little?

@timkindberg
Copy link
Contributor

Yes better, because now I understand generally what it is you want to achieve. Could you flesh the code out a bit more? Where would you plug the templates in? What would the templates look like? Sorry I can't give you a definitive answer yet, its still to early to tell if its going to achieve the result you are looking for. My gut is saying that that code is not going to do what you want it to.

@fxck
Copy link
Author

fxck commented Aug 20, 2013

Well I'm somewhat sure the code, as I posted it, wouldn't work.

Ideally it should be like this

state('carousels', {
  abstract: true,
  template: '<h1>Cats and dogs</h1>'
+  '<div ui-view></div>' // this would/should be for cats
+  '<div ui-view></div>' // this would/should be for dogs
})
  .state('carousels.cats', {
    url: '/carousels?catPage'
    template: '<a href="" ng-click="setStateParamCatPageToPlusOne">next set of cats</a><img ng-repeat="cat in catsOnThisPage" />'
  })
  .state('carousels.dogs', {
    url: '/carousels?dogPage'
    template: '<a href="" ng-click="setStateParamDogPageToPlusOne">next set of dogs</a><img ng-repeat="cat in dogsOnThisPage" />'
  })

But afaik you can't have two ui-view on the same "level", and that's the whole problem..

@jeme
Copy link
Contributor

jeme commented Aug 20, 2013

@fxck You are able to have several views on a single state, you need to use named views and the views property in this case. https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views

But it really sounds more like you should use a single view and make one or more custom directives instead.

@fxck
Copy link
Author

fxck commented Aug 20, 2013

@jeme named views wouldn't work in this case, since you can't give a named view an url param(or can you?). Directive would work indeed, it would be pretty simple if I didn't want those params visible in the url. But I do..

@jeme
Copy link
Contributor

jeme commented Aug 20, 2013

There is a ton of ways you can make a directive be based on url parameters. One would be to access the location service in the directive and pull parameters from there, this is rather direct and might not be the most wanted way. But depends on the implementation still.

Another is to use data-binding and instruct a directive to "watch" for changes on that parameter, then through a controller grap those parameters from either $location or some other relevant source.

So... In other words... you could still easily use directives...

The thing is, using "views" for components is not really what they are meant for, and it makes your application much more complex than what it has to be, a directive is declarative and easy to use... Granted there is a learning curve for you to implement them, but I would not recommend using views for components, they are for larger aspects of the application.

@fxck
Copy link
Author

fxck commented Aug 20, 2013

Well that's new for me, looking at this https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views each of those(graph, table, filter), looks like a component to me.

Either way, thanks for the information, I didn't quite realize you could use $location.

@timkindberg
Copy link
Contributor

Yeah ok, @jeme is right. What you are trying to do may not be possible without a decent amount of custom work and a very good understanding of how ui-router works behind the scenes. What you want isn't included out-of-the-box.

There is some talk about not refreshing the whole view when parameters change in the future (or an option for it) but its not in there yet.

@jeme
Copy link
Contributor

jeme commented Aug 20, 2013

It's just an advice...

I would use ui-router for the bigger parts and groupings of your application where there are more clear cuts, but date-time selectors, carousels, accordions, tab-views and so on are more what Directives do well...

A While back we tried to think in the same lanes for tab views, but it's just not "smart"... it becomes strange and hard to understand, and not even possible where you don't wan't them to reload each other.

So, what I would do was to let UI-Router load a single view, defining both carousels just on the url /carousels and then bind the state of the individual carousels to the desired "optional" url parameter...

You may be able to get UI-Router still do some of the work for you by defining "empty" sub-states. But I think it would be just as easy to either use the $location service or bind to it's parameters as it sounds to me like you wan't the parameters to be more independent of each other making the following:

/carousels?cat=10
/carousels?dog=6
/carousels?cat=5&dog=8
/carousels?dog=10&cat=12

Valid?

One way would be to add a controller to the view to do something like:

angular.module('carouselapp').controller('carouselsController', ['$location', '$scope', function(loc, sc) {
  $scope.$on('$locationChangeSuccess', function() {
    $scope.currentCat = $location.search().cat;
    $scope.currentDog = $location.search().dog;
  });
}]);

Then the view would be something like:

<carousel id="catCarousel" current="currentCat" data="cats" />
<carousel id="dogCarousel" current="currentDog" data="cats" />

It is unfortunately needed to jump to the location service as UI-Router doesn't have the same concept as the original router of sending an "update" out when search parameters changed... (Not As far as I know at least)

@fxck
Copy link
Author

fxck commented Aug 20, 2013

Just a side note, I used carousel because I thought it would be easy to imagine, but in my actually case the first "component" is a graph of the current week, with possibility to move to previous/next week etc, each day also serves as a tab, second "component" is a huge ass table of entries for the selected day..

@ksperling
Copy link
Contributor

This sounds like the "components as independent state machines" feature that was discussed previously. Essentially there's a high level of complexity in this because (1) the global state machine will effectively contain "orthogonal regions" that can be active at the same time (2) mapping of URLs to states and back also becomes much more complex as in this model any node in the state tree that represents an orthogonal region has to ask each sub-region for a URL and somehow munge this into one URL, and undo the process on the way back in.

@nateabele
Copy link
Contributor

I think this is out-of-scope for the project in its current incarnation.

@jeme
Copy link
Contributor

jeme commented Aug 21, 2013

Well I don't think this would be a feasible feature in any future versions either, it seems to complex to create something generic around it, also I am afraid that it would allow people to generate something that gives horrors.

What I think UI-Router could do was to allow for a similar feature as we saw under the Router, where you had RouteUpdate to allow the developer to hook into parameter updates that was sort of "optional" (in the original router that was search parameters only AFAIK).

But that would allow you to trap that event in a parent controller and update scope fields and as an effect have directives like tab-views and so on bound to that... sort of like outlined above, but we could provide a much cleaner hook for it... E.g. something like $state.onRefresh(fn) or $state.on('refresh', fn) or something similar, And I believe that would go a long way to enable the above without having to support merging urls or something horrible like that in UI-Router, it would be left to the developer what it means when the parameter is not available, often that would be First Tab, First Image ect, but that is for them to figure out.

@cm325
Copy link

cm325 commented Aug 29, 2013

Does anyone have an example of using a directive that would respond just to hash changes/html5 state events, so that we could implement having two active states in the url?

@fxck
Copy link
Author

fxck commented Aug 29, 2013

It's basically what @jeme posted

  $scope.$on('$locationChangeSuccess', function() {
    $scope.currentCat = $location.search().cat;
    $scope.currentDog = $location.search().dog;
  });

$location.search() contains an object with all the state params

@cm325
Copy link

cm325 commented Aug 29, 2013

ah whoops, yes, I see now, thank you!

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

6 participants