-
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
how do you handle state for a modal dialog ? #123
Comments
The states for the dialog would need to be children of the detail.item.edit state. Is the dialog used from multiple different states or just from the one? In that case you'd need separate copies of the dialog's own states attached to each of the states from which it can be used. This is not something that's supported out of the box at the moment, but you can write your own helper function that takes $stateProvider and the state to attach to and then generates the dialog states underneath that state. It's hard to see how you'd do all this purely from a directive, or how it would even interact with the directive. To support that sort of thing the whole state transition logic would effectively have to trickle down the scope tree, with each layer handling parts of the parameters or URLs, but there would be a large number of challenges with such an approach. |
eventually, the dialog will be called from different states. If I follow you right, I'll have to add the dialog states as children of the caller's state, leading to duplicated states across the application if I don't use the method you've described. For now, I'd say it's not an issue. Can't you use the same templateUrl for different named states ? I guess I'll just have to name the dialog states differently according to what parent they are attached to. It'd be great if that scenario was supported by the ui-router plugin, as dialog modals are widely used. |
@graphicsxp yes, that's right, all the dialog's states would be inside the caller's state, and while the user is navigating through the dialog the caller state would remain active. It seems cases like this could benefit from having a way to define a "blueprint" for a re-usable sub-tree of states, which can then be "instantiated" in multiple places in the state tree. Maybe we could call it a "component" instead of a blueprint, and allow them be bound to a view in a state, along the lines of
The root state of the component would effectively need to be merged into the calling state, and child states of the component become children of the calling state (if we'd want to allow multiple components to be attached to the same state we're going to have to look at what's effectively "orthogonal regions" in the state machine, but then this is a feature that's been hinted at a few times) All URLs within a component would need to be relative, so that they can be combined with the URL for any parent state. Because the root state of the component gets merged into the caller, it can't have it's own URL (or state parameters), but it's children could. We'd probably have to do some magic so a component gets it's own separate namespace for parameters and it's own $stateParams. In fact if we do allow multiple "orthogonal" components, the calling state might have to explicitly provide a URL parameter that the entire URL of anything that happens within the component's own state machine gets serialized into, e.g. the calling state's URL pattern might look like '/abc/{abcParameter}/?details' if using a query parameter, or '/abc/{abcParameter}/{details:.*}' with a path parameter -- with the convention being that the parameter name matches the name of the view that the component is instantiated into. In most cases there would have to be some encoding/escaping (e.g. percent-encoding) applied to the component's URL, depending on how it's inserted into the parent URL. (I think I'll stop here... there's a lot more detail that would need to happen)... @jeme what are your thoughts on this? |
This component idea is exactly what I'm looking for. Big +1. I've had to embed my 'component' as an iframe right now, but that causes all kinds of problems. |
I'd like to have something like this. In the back of my mind I've been thinking about how to load components that are defined in modules that aren't depended upon by the app module. It might take the form of having the components implement a specific interface so that the app module wouldn't need to know the particulars, and using some sort of div that is defined outside of the application but is absolutely-positioned into place within the app, along with some message busing between the two. I wonder how this might fit into that idea, if at all. |
A more basic question about modal dialogs; has anyone successfully been able to use ui-router together with ui-bootstrap's modals/dialogs? If so, are there any Plunkr examples somewhere? |
@stu-salsbury If you find a way to load modules and activating them (e.g. if they include things like directives) without having to depend on them directly, I am very interested to hear about your findings, because we are about to develop a prototype where we will allow for deployment of add-ins, each add-in would be in a module by it self naturally, and so far we figured that we had to dynamically add those to the main module on page load. @ksperling to be honest, for "generic states" that are to be fitted under existing states, I have been satisfied with #95 as a workaround solution that was ok... But that fits into cases where it is natural that even if they are common states (e.g. Edit, Delete, Create for different type of records)... They are still essentially different at least from a conceptual standpoint... However, general error dialog etc... here the idea of having In any case, I would be in favor of separating this out into maybe a And since you would probably not define your components along side of your states, I think that would be ok to do. |
@jeme @ksperling I like this idea of "components"! +1 It's like there are several big ticket features that we are seeing users ask for and that I think will ultimately be needed to consider our router the ultimate solution. The ones that come to mind now are serialization of state to params and this component feature. @jeme, I believe you are using the word "properly" when you mean "probably". I've seen it several times, so I just wanted to mention it. |
@timkindberg I would like to reiterate my support for components and say that this could be very complementary to the idea of providing a decoupled state serialization approach. The architecture could be a series of named, but independent root state trees, wherein each state tree can provide its own state serialization / decoding methods (aka: router). The behaviour of the current code would be replicated by having a default, unnamed base state tree that uses the corrent url-based serialization / decoding. I think that this approach would allow custom serializer / decoder services to provide the kind of modular 'blueprint' you mentioned above and be able to tack that on at specified states. $stateProvider.module("reusableCRUD", {
router: $subordinateToStatesRouter(['packages.item', 'widgets.detail.item'])
})
.state('create', ...)
.state('update', ...)
.state('delete', ...); Perhaps I'm dreaming in technicolor though ;-) |
@jeme, I don't think there is a way to load an addin into the module after module definition. The solution I have in mind is to make the parent app depend on something you might call the "compoponentManager" module and the add-in apps depend on something you might call the "component" module, and then using jquery to keep them positionally cohesive and using eventing (through a dom node or nodes that is/are in neither the app module nor the add-in modules. So not too clean. However, hopefully it would be performant (enough) and easy (enough) to code to once it's available. It's not on my short term list, though. Because such add-ins would want to participate in routing, I mentioned it here. |
I've done this. I ended up triggering the dialog in I'll try to condense it to an example tomorrow. |
On the 'components' front, I've done something where I provide my application's base module with a list of all potential dependencies that various different controllers used, and it would iterate through those, checking for the existence of each one in a try/catch block, adding them to the base module's dependency array if they existed. That way, if your dependencies vary by page or whatever, at least you can always load in the ones you need based on which other scripts you load. The main trick is, obviously, you need to know all possible dependencies up-front. My developers and I are taking a week off at the end of may to do some Open Source work we've been meaning to get to. I can see about adding hooks to the module API if there's sufficient interest. |
Y... I hate those two words... English being my second language, i switch similar words around sometimes... >.< |
It's hard to see how you'd do truly dynamic (i.e. after the app is already initialized) loading of angular modules, given how $injector works. E.g. a new module you load could try to $provide.decorate() a service in a module that's been loaded earlier and has already been instantiated, which wouldn't be possible because you've already handed out references to the un-decorated instance to the code that's already running. It seems to me you'd have to have some kind of setup where each dynamically loaded "sub-system" (I'm avoiding the word "module" on purpose) gets it's own injector and has some way to access a parent/global injector to access services like an event bus that allows the sub-systems to communicate. How you mix together the views from the different sub-systems is yet another question though. |
@ksperling It can be done, we have the technology. ;-) https://gist.github.com/nateabele/5610305 |
@nateabele Thats just Lazy loading of modules/components isn't it? Where you still know the components/modules up front as you register them in We are (at least some of us) talking more in a plugin model where the components/modules you depend on change over the cause of the applications lifetime... Obviously this is still doable if you just inject the "config object"... But that means that deployment of a new component also means reload of the browser. I am perfectly ok with that, and when it comes to a real plugin model, I think there is more important things I need to figure out before that one reload of the browser becomes something I wish to solve (if even possible)... After all... |
@jeme That's correct. I should have clarified, this is only a first pass at it. However, |
@nateabele -- are you saying that autoLoad will let you load a new module as a dependency at runtime? From the followin comment in the code I assumed it wasn't possible:
I'd be really excited to be able to add dependencies on the fly/lazily. I just don't know how to interpret your comment about periodically reloading from JSON... |
@stu-salsbury Sort of. This is just a first step. All it really proves is that you can get Angular to load a module's dependencies dynamically, instead of throwing an exception and bailing out. Now, automatic module loading is not the same thing as lazy module loading. Obviously, this would be the next step. The trick is, Angular makes it difficult to 'know' what module a particular controller/service/whatever is supposed to come from, since all resources exposed by a module get thrown into a single app-wide 'namespace'. If it was possible to know up-front what module each of a controller's dependencies was defined in, you could theoretically defer execution until they're all loaded, but again, unless you know up-front where everything is defined, you have to load everything before you can do anything. The only way around this I can think of is extending injector annotations with extra syntax, such as: /* ... */
.controller("ItemsController", [
"moduleA.serviceB", "moduleB.serviceA", function(serviceB, serviceA) {
/* ... */
}
]); We're getting a little off-track here, but if anybody has any thoughts or would like to help work on this, find me in #angularjs on Freenode. @graphicsxp I posted my example code for a state-based modal here: #133 (comment) |
@nateabele maybe an interesting idea would be to generate a 'map' of exported services / controllers of each module at build time into a JSON format of some sort, so that initially you'd only have to load that metadata rather than the actual code. |
@ksperling Yeah, that was my other thought, but I'm not as familiar with hacking |
Well $injector is generally lazy, so as long as you don't use anything that depends on anything that you want to load lazily you should be fine. Anything that wants to talk to a provider or participate in configuration would have to be eager though |
Right, the cases I'm thinking of are:
That's the big one, IMO. The other solution that @stu-salsbury came up with was allowing states to have i.e. a |
Closing this, as the original issue has been resolved. I've created a new issue where we can continue the discussion on module lazy-loading: #146. |
i have one idea: in the controller you can define status of modal (open / closed) and pass params to it; i.e. stage: 'a' , 'b' ... And if it is derictive - you can just pass paramter to it from controller |
I use ui-router for handling state management in my application.
In one of my states, the view contains a form for filling data about a Contract and inside that form there is a directive .
The directive contains a search button for opening a modal dialog. Inside that modal, user can search for a person and results are displayed in a paged table. The user can then select one of the results to close the modal and to assign the selected person to the Contract.
This works, but then I want to handle browser history
So the state for the Contract form is defined as followed:
$stateProvider.state('detail.item.edit', {
url: '/edit',
views: {
'@detail': {
templateUrl: 'myModule/views/contract.detail.item.edit.html',
controller: 'contracts.detail.item.ctrl'
}
}
I can't figure out how to add a state for the modal. Plus it is opened from a directive, which makes things trickier. When the modal is closed, the Contract state should not be reloaded either.
Has anyone attempted doing that already ?
The text was updated successfully, but these errors were encountered: