FWIW, I have now found an acceptable solution. I just wrapped my whole modal in a component. That is, the actual template file (let’s say templates/dialog-settings.hbs) looks like that:
{{dialog-settings closeAction='removeDialog' update='update' deactivate='deactivate'}}
And then I define the actual contents in templates/components/dialog-settings.hbs.
I keep everything related to internal state (input fields, validations, etc.) in the component, so it’s not persisted when closing the modal, since components are not singletons. Then for every action that changes application state, data, etc., I just do something like:
actions: {
update: function() {
this.sendAction('update', this, this.get('password'));
}
}
and the real update action is handled by the controller (I guess it would be more proper to do it in a route, but with this solution, modal dialogs don’t have routes and I don’t want to throw everything in the application route).
I also pass in a reference to the component itself, so the controller can do something like:
user.save().then(...).catch(function() {
component.set('error', 'update failed');
}
I think this works quite well and also keeps responsibilities isolated, but it needs some amount of boilerplate (especially passing in attributes and actions to components) which is a minor nuisance. Nevertheless it does solve the problem.
I guess some of this can be handled more nicely when routable components become a thing.