Skip to content

Dynamically modifying state views #379

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
eahutchins opened this issue Sep 3, 2013 · 32 comments
Closed

Dynamically modifying state views #379

eahutchins opened this issue Sep 3, 2013 · 32 comments
Labels
Milestone

Comments

@eahutchins
Copy link

I've got a use case for being able to alter the views associated with a state dynamically, after the state has already been defined. I also see some confusion between state and the views under a given state, it seems if you'd really like to use the views to allow reusable components (say a bunch of different types of graphs in a business dashboard app) you'd like to avoid having to define a new state for every possible permutation of view that the user might select. To clarify:

nested views

Imagine the user had the ability to select which types of views are being used for the table and graph, but that this selection was transient and not something the designer wanted to be able to navigate to. Currently this seems really hard to get working with ui-router, what I'd like is to be able to do something like:

$state.setViews({ 'viewname': { ... } })

This also brings up the issue of why states can only have templates or views, but not both. What was the thinking behind this restriction (or is it only an implementation limitation)? The use case I'm working on would benefit from being able to populate the current state's template with reusable components in named views (as well as those of any parent state). Am I missing some big drawback to being able to do this?

@timkindberg
Copy link
Contributor

This also brings up the issue of why states can only have templates or views, but not both

What do you mean by that comment? In my mind templates plug INTO views, its not a one or the other decision.

@eahutchins
Copy link
Author

This also brings up the issue of why states can only have templates or views, but not both

What do you mean by that comment? In my mind templates plug INTO views, its not a one or the other decision.

I guess I find it weird that a single state can either specify one of template*: or views:, but not both. I get that states "plug in" to their parent's ui-view, but views: can target named ui-views absolutely, so why couldn't a state have a bunch of named ui-views in its template and fill them from views:? This might seem silly at first glance, but if the named ui-views are used to lookup reusable components (by name or via additional directives) it becomes pretty useful. You can get something like this working as-is by synthesizing child states and populating those, but then you end up with complicated navigation in your ui-srefs. In general being forced to add states to alter the UI seems to go against using state to structure your application, they're really orthogonal things.

@nateabele
Copy link
Contributor

I've got a use case for being able to alter the views associated with a state dynamically, after the state has already been defined.

How soon after? Does this necessarily have to happen at runtime, or would some deferred time still within config-time work?

I also see some confusion between state and the views under a given state

I know. View targeting is pretty much the worst part of this library. Now that I've factored out $view into a separate service, I need to finish my view-targeting rewrite. I'll try to get to it in the next day or two, and you can let me know what you think.

[W]hat I'd like is to be able to do something like: $state.setViews({ 'viewname': { ... } })

Yeah, under the API I'm working on, you'll be able to do something along the lines of $view.load("fully.qualified.view.name", { template: ..., controller ... }). Combine that with an onEnter hook in your desired state, and you should be able to have fully 'polymorphic' view support.

I guess I find it weird that a single state can either specify one of template*: or views:, but not both.

That's not entirely accurate. If you don't specify a views key, it is generated implicitly by grabbing the requisite keys (template*, controller, etc.) from the top level of the state configuration object, in order to generate a empty-named key within views. This "default" view configuration is then used to target an unnamed ui-view within the parent state.

@laurelnaiad
Copy link

I have such use cases, too. I'd love it if run time state definitions and redefintions (essentially last minute) were supported along with hierarcical definitions... my dream would be to retire angular-detour but in the short run my goal is to update it to match ui-router 0.2.0 :)

My current use case requires fetching state definitions and components from outside the app. With detour and couch potato this is straightforward but upgrading it to 0.2.0 isn't going to be terribly enjoyable, even though it will be worth it -- @nateabele thank you very much for all of the work you put into it.

Fwiw I think of a state as being a set of view specs each of which specifies a controller (optional) and a template. It doesnt much matter how many there are or what their names are. It seems quite similar to what @eahutchins is doing. You can achieve this pretty easily by laying your own service on top of ui-router.

@laurelnaiad
Copy link

For the issue of having either a template or views, I think the solution that works today is a view with a name equal to empty string. If you always use this tecnique you can at least be consistent.

@eahutchins
Copy link
Author

I've got a use case for being able to alter the views associated with a state dynamically, after the state has already been defined.

How soon after? Does this necessarily have to happen at runtime, or would some deferred time still within config-time work?

Runtime, as the user is interacting with the app or external events are happening (like orientation change or window resize).

I also see some confusion between state and the views under a given state

I know. View targeting is pretty much the worst part of this library. Now that I've factored out $view into a separate service, I need to finish my view-targeting rewrite. I'll try to get to it in the next day or two, and you can let me know what you think.

Great!

[W]hat I'd like is to be able to do something like: $state.setViews({ 'viewname': { ... } })

Yeah, under the API I'm working on, you'll be able to do something along the lines of $view.load("fully.qualified.view.name", { template: ..., controller ... }). Combine that with an onEnter hook in your desired state, and you should be able to have fully 'polymorphic' view support.

This is pretty much what I've started to attempt to implement... while you're at it could you add a reload option to transitionTo to force re-rendering the target state?

I guess I find it weird that a single state can either specify one of template*: or views:, but not both.

That's not entirely accurate. If you don't specify a views key, it is generated implicitly by grabbing the requisite keys (template*, controller, etc.) from the top level of the state configuration object, in order to generate a empty-named key within views. This "default" view configuration is then used to target an unnamed ui-view within the parent state.

I understand, the problem I'm running into is that you always need a child state to fill in the views of a state, i.e. there's no way to have a current state with ui-views with those views filled in. If views: populated templates starting at the current state instead of the parent it would simplify things for me (for example all of the ui-srefs have to do ^.^.sibling.views in order to get to another properly-rendered sibling state). It would also be nice to support relative addressing in the view name, so you could do view@^.^ to modify a grandparent's ui-view.

edit: I guess for this to work the views: would have to search up the state hierarchy to find matching ui-views instead of being hard-coded to view@parent, or be able to name them view@. to modify the current state.

@timkindberg
Copy link
Contributor

Pretty sure you can have a state populate its own views. I've seen
others do it but we yelled at them at the time because it wasn't
designed to work that way. You just need to use absolute view names
"viewInThisState@thisState"

@eahutchins
Copy link
Author

@timkindberg yeah actually with @nateabele's $view.load() I'll have everything I need in order to populate leaf states without an extra child state, I was thinking I could do it in one shot with a clever config but state definition and view loading are going to have to be separate in my use case for other reasons anyway. For reusability it would still be nice to be able to say "viewInThisState@.", I think the only change needed would be to always populate the state's views with the default unnamed view from the state first (from the state definition itself) and then overlay anything defined in the config's views. In that scenario if you defined an unnamed view in views you'd be able to have the other named views inserted into that.

@timkindberg
Copy link
Contributor

@eahutchins ok, but you also know that you should also be able to do it currently without needing a $view.load()?

@eahutchins
Copy link
Author

@timkindberg right, but not dynamically at runtime (which is the part I really need).

@eahutchins
Copy link
Author

@nateabele how are things going with the $view.load API?

@nateabele
Copy link
Contributor

@eahutchins Heh, good timing. Just finished the second round of refactoring, and I'm testing it in my production app right now actually.

@dagahe
Copy link

dagahe commented Sep 11, 2013

Hi, i´m trying to port one old jsp application to angularjs. Our idea is a tab based application (like gmail). While a user is showing a list of items (List items tab), the user can click on one item to edit it, the application will open a new tab and render the edit form in this new tab (Item 1 tab). The user can click again over the "List item tab" and click to edit other item and then a new tab for edit this item is created. I have created a tabsService listening on stateChangeSuccess to open new tabs on state change, but i don´t know how to assign at runtime the correct tab. Each tab is created with a inner div and ui-view="statename".

Is possible use the new $view service to implement this requirement?.

@nateabele
Copy link
Contributor

@dagahe $view is a lower-level service for which the primary use case is doing your own template/layout handling independent of $state.

@nateabele
Copy link
Contributor

@eahutchins Sorry, I'm a space cadet. I just realized that the $view service as it exists currently should already work for you. The refactoring I'm doing will just simplify the view targeting syntax.

@nateabele
Copy link
Contributor

@eahutchins Based on my comment above, this issue should actually already be resolved. You can give it a try this way:

$view.load("view.name@state", {
  templateUrl: "/partials/foo.html",
  controller: "MyController"
});

Let me know if you have any trouble with it, and we can reopen this.

@eahutchins
Copy link
Author

@nateabele thanks, I'll give it a try.

@eahutchins
Copy link
Author

@nateabele I think I've run into a problem: I can only load views which were mentioned at state definition time (in views:), or at least the state has to be transitioned to (assuming I dynamically add configs to the state's views:). It would be great to have a $state.reload() for this and other reasons (basically force a transition to the current state).

edit: I should clarify, I'd like to be able to have templates with named ui-views in them which can be loaded with $view.load() dynamically while the state is current.

@eahutchins
Copy link
Author

To clarify further: just like you can programmatically transition to states declared outside of your immediate context using names as dynamic bindings, you should be able to programmatically change UI configuration (views) at any time without knowing anything other than the name of the binding. The template should be the ground truth of what views are available to a state because that's really a declarative thing, not something you want to hard-code into potentially generic scripting at state declaration time. Named ui-views might be dynamically added and removed via DOM operations, for that matter. Each state can address any view in its resolved template as well as any parent state's views, and any parent state can set a "default" view for all child states (overridable by the child).

Example uses might be a $scope.loadView() method or a directive which allows the designer to specify view configuration in the template itself, avoiding any scripting at all for the common case were you just want to instantiate a reusable component.

@annasaheb4770
Copy link

do you have any sample code which proves this functionality, We wanted to do implement something similar.

Any input is appreciated.

Thanks

@vb8190
Copy link

vb8190 commented Feb 16, 2014

We are facing a similar issue where we need to define the view names dynamically? @eahutchins : did you get any answer on this?

@annasaheb4770
Copy link

I am sorry but no answer yet, this is one of the major issue we are facing with angular. We have to have show multiple views in same page dynamically.

@timothyjlaurent
Copy link

bump

@annasaheb4770
Copy link

We finally ended up using ng-include and change it's template using $templateCache, something like shown below:

HTML:
div ng-include="maintViewTemplate"

Controller js code:
$scope.maintViewTemplate = "<>"; //specify initial url for template

// Call this function when you want to reload template with different url.
$scope.reloadTemplate = function(newUrl) {
$templateCache.remove($scope.maintViewTemplate);
$scope.maintViewTemplate = '';
$scope.maintViewTemplate = newUrl;
}

Thanks

@timkindberg
Copy link
Contributor

Is #894 kind of what you might be looking for?

@shanealsingh
Copy link

Any updates on this?

@willchavez
Copy link

@nateabele, @timkindberg Hey, beginner ui-router user here. This might be a dumb question and I'm not really sure if it's been mentioned. Are we able to pass in state parameters to this function so that the view that's rendered reflects a different set of data?

for example something like this...

$view.load("view.name@state({num:10})", {
  templateUrl: "/partials/foo.html",
  controller: "MyController"
});

as opposed to

$view.load("view.name@state", {
  templateUrl: "/partials/foo.html",
  controller: "MyController"
});

I was looking at the view.js file and I saw that it supports template parameters, but I'm not really sure if this is what I'm thinking of.

Any input would be appreciated.

@nateabele
Copy link
Contributor

@wchavezsalinas You can pass parameters, but not like that. See https://github.com/angular-ui/ui-router/blob/master/src/view.js#L31-L34

@plong0
Copy link

plong0 commented Jan 14, 2016

wondering if there was ever a clear answer on this?

$view.load doesn't seem to do anything in v0.2.15. I tried it with fully qualified view names ('view.name@state'). I even tried updating to 1.0.0alpha0 and $view.load gives the error "viewConfig.hasTemplate() is not a function"

I tried using var state = $state.get('view.name'); and then altering state.views, but that doesn't work either.

All I'm trying to do is change the template property after the state has been defined ... for my use case it doesn't matter if it's in config or run, but run seems easier because we have the $state and $view services there.

@christopherthielen
Copy link
Contributor

Changing a template string at runtime is best done with templateProvider

@plong0
Copy link

plong0 commented Jan 15, 2016

Thank you. But is there any way to change the templateProvider function at runtime?

I know I could refactor the code so the original templateProvider resolves a different template, however in this case it would be much simpler if I could simply change the templateProvider function.

@ghost
Copy link

ghost commented Mar 7, 2017

Hello @nateabele @christopherthielen I have similar situation. I want to chage the content of named view at runtime or similar. This is my question:
http://stackoverflow.com/questions/42657489/angular-ui-router-change-content-of-named-view
I tried using $view.load() but not working. I'm using ui-router 0.2.18. Thanks

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

No branches or pull requests