Skip to content
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

Dialog design #131

Closed
tb opened this issue Oct 4, 2016 · 25 comments
Closed

Dialog design #131

tb opened this issue Oct 4, 2016 · 25 comments

Comments

@tb
Copy link
Contributor

tb commented Oct 4, 2016

I have recently replaced md2-dialog with mdl-dialog. It however creates a lot of extra effort compared to usage of md2-dialog which did not require service / vcRef and I was able to use it with just the directive.

This is example how I was able to use it to build a Confirm component:

<mdl-dialog #confirmDialog>
  <div class="mdl-dialog__content">{{ message }}</div>
  <div class="mdl-dialog__actions">
    <button type="button" class="mdl-button" (click)="action.emit()">Yes</button>
    <button type="button" class="mdl-button close" (click)="confirmDialog.close()">No</button>
  </div>
</md2-dialog>

<div (click)="confirmDialog.show()">
  <ng-content></ng-content>
</div>

I have used this component by wrapping remove icon like this:

<confirm
    title="Remove Post"
    message="You are you sure?"
    [skip]="!post.id"
    (action)="remove.emit()">
  <mdl-button mdl-button-type="icon">
    <mdl-icon>cancel</mdl-icon>
  </mdl-button>
</confirm>
@tb
Copy link
Contributor Author

tb commented Oct 4, 2016

I checked now also how dialog behaves after change from

constructor(private dialogService: MdlDialogService,
            @Inject(forwardRef(() => AppComponent)) private app: AppComponent) {
  this.dialogService.setDefaultViewContainerRef(this.app.vcRef);
}

to

constructor(private dialogService: MdlDialogService,
            public vcRef: ViewContainerRef) {
  this.dialogService.setDefaultViewContainerRef(this.vcRef);
}

The end result is modal that can not be closed because dialog-backdrop is added to body, and modal is not.

I checked how the md2-dialog works. It also adds backdrop to body, but it also adds the modal inside. See screenshot:

screen shot 2016-10-04 at 11 04 40

@mseemann
Copy link
Owner

mseemann commented Oct 4, 2016

  • that the dialog can not be closed is an issue.
  • md-dialog adds all overlays to the body with this code:
    let container = document.createElement('div');
    container.classList.add('md-overlay-container');
    document.body.appendChild(container);

i have tried to find a solution without accessing the body element.

@tb
Copy link
Contributor Author

tb commented Oct 4, 2016

Having to import AppComponent from other components complicates usage of the dialog, especially for tests.

It would be great improvement to have setDefaultViewContainerRef some default value (maybe the applicationRef.componentTypes[0]?)

I see its also related to angular/angular#6446 (comment)

If this is resolved, this opens way to define modals just in template like the md2-dialog example.

@mseemann
Copy link
Owner

mseemann commented Oct 4, 2016

i know this approach. but this requires that you inject the ViewContainerRef to the Root-App-Component and you don't know why you do this. with setDefaultViewContainerRef i wanted to make this obvious and not so magic :-)

@tb
Copy link
Contributor Author

tb commented Oct 4, 2016

It possible to remove the need for @Inject(forwardRef(() => AppComponent)) private app: AppComponent) if we set this.dialogService.setDefaultViewContainerRef(this.vcRef); right in the AppComponent similar to how its done in angular2-modal setup I can make PR with this change in evening.

I think it should be possible to make something like <mdl-dialog #confirmDialog>. I am thinking to make component similar to mdl-dialog-component, just use ng-content as template. Let me know what you think of it.

@mseemann
Copy link
Owner

mseemann commented Oct 4, 2016

sure! this is possible. but then we need to make it more clear that this: this.dialogService.setDefaultViewContainerRef needs to be done in the appcomponent. this is the reason why this is done in the dialogDemo-Component. but this dosen't help either. look here: http://stackoverflow.com/questions/39847890/use-dialog-is-very-complicated :(

i agree that this <mdl-dialog #confirmDialog> would be a nice way. the perfect way would be that it is not required to specify a viewcontainerref at all.

in the current master there are already some changes to the dialog api...

@amcdnl
Copy link

amcdnl commented Oct 4, 2016

@tb @mseemann - I had a similar challenge for a dialog component I built; you can see how I solved that here - https://github.com/swimlane/swui/tree/master/src/components/dialog

@mseemann
Copy link
Owner

mseemann commented Oct 4, 2016

@amcdnl yep. so far there are three ways to solve this problem: 'how can i append components to the root application or even better to the document.body' (or variations and combinations of these solutions):

  • use something like this this.applicationRef['_rootComponents'][0]['_hostElement'].vcRef; (or this.applicationRef.components[0].instance)
  • access the document.body directly - e.g. document.body.appendChild
  • provide the viewcontainerref of the app by the dialog user

every approach has advantages and disadvantages. right now there seems to be no way in angular2 to achieve this with a solution that "looks right" :(

@tb
Copy link
Contributor Author

tb commented Oct 4, 2016

@mseemann For me this.applicationRef['_rootComponents'][0]['_hostElement'].vcRef seem ok, and looks the current official way of doing it. Many projects are doing it. Provide the viewcontainerref of the app could be left as an option if you see some use case for that.

As to second aspect of this ticket - defining <mdl-dialog #confirmDialog> I have made WIP PR.

The current custom component creation does not allow binding @Inputs and @Outputs so i.e. if I want to use modal to edit some object, maybe call some callback back on parent component, I can not do it ATM. The <mdl-dialog #confirmDialog> should fix that.

@mseemann
Copy link
Owner

mseemann commented Oct 5, 2016

@tb i am little bit short of time the next two days. give me some time to think about it. The current design is the way how i usually use such a ui component like a dialog. You are right. you can not use Input or Output. I'm doing this by using a service.

about this line: this.applicationRef['_rootComponents'][0]['_hostElement'].vcRef. for me this is something i would never write. this code can fail every time a new release of angular is out because it uses private api and array indexes. if i'm right the only thing misko confirmed was that the first component is the root application. also: the typescript compiler would never warn you if the api changes. this code compiles anyway and crashes at runtime if they change something.

For your further development: just keep this problem as solved and that there is a way to get the viewcontainerref :) I'm sure we will find a solution.

@mseemann
Copy link
Owner

mseemann commented Oct 5, 2016

just a thought to discuss: what if we add a dialog-outlet to the html - something like <router-outlet>:

<body>
    <app-root>...</app-root>
    <dialog-outlet></dialog-outlet>
</body>

how does this "feel"?

@tb
Copy link
Contributor Author

tb commented Oct 5, 2016

@mseemann I am not sure about dialog-outlet. For me its fine to do just this: 005062b ;-)

@mseemann
Copy link
Owner

mseemann commented Oct 5, 2016

@tb but it looks so cool! ;-)

@tb
Copy link
Contributor Author

tb commented Oct 5, 2016

@mseemann Yeah, its a little cooler then dialogService.setDefaultViewContainerRef(this.app.vcRef); but will work only when you use router, right? BTW I think I have seen this approach somewhere... probably in Ember https://guides.emberjs.com/v1.10.0/cookbook/user_interface_and_interaction/using_modal_dialogs/

@mseemann
Copy link
Owner

mseemann commented Oct 5, 2016

No, no. It's comepletly independent of anything. Sure a little bit complicated to implement. But easy to use. And you can deside where the dialog will be attached to the dom.

I havn't developed anything with ember. But it looks really similar...

@grizzm0
Copy link

grizzm0 commented Oct 7, 2016

Why don't you simply use the <dialog> element? Sure, it only works in chrome right now. But material-design-lite itself uses the dialog element and recommends a polyfill if you're going to use it in any other browser.

Dialog comes with a new pseudo element called ::backdrop which solves the backdrop automatically when showing a dialog as modal.

See: CanIUse - MDN

@mseemann
Copy link
Owner

mseemann commented Oct 7, 2016

@grizzm0 sure i thought about it. but there are a lot of reasons why i prefer the old-way:

  • as you have stated - you will need a polyfill
  • you don't know what other browsers will provide in the future. most of the ui will be up to the browser vendor what he prefers - see the alert and confirms dialogs for example. you'll never get a consistent ui over a broad range of browsers/devices
  • the problem we are discussing here is a general problem. not only related to dialogs. also snackbar, tooltips, menu and even any kind of popover would benefit from a general solution: how to take a component out of the declared flow and put it in different dom position by keeping all the advantages of angular.
  • ...

@grizzm0
Copy link

grizzm0 commented Oct 7, 2016

As this lib is basically a wrapper around material-design-lite for angular2 I would suggest using the same behaviour. In this case using the dialog element.

Another issue here is provides: []. I've got a shared dialog to be used in multiple places. I inject a data object into the custom dialog to be updated on submit. I need to set the provides key in the application component as that's where it's executed from.

So I have no way to provide different data objects for the same dialog component used in different components.

@tb
Copy link
Contributor Author

tb commented Oct 7, 2016

@grizzm0 I think you are not aware of this WIP

@mseemann
Copy link
Owner

mseemann commented Oct 7, 2016

@grizzm0 can I see any code what you are doing there:

Another issue here is provides: []. I've got a shared dialog to be used in multiple places. I inject a data object into the custom dialog to be updated on submit. I need to set the provides key in the application component as that's where it's executed from.

what you are mean with

provides: []

?

@grizzm0
Copy link

grizzm0 commented Oct 7, 2016

Sorry, I meant providers, not provides.

If I set the default viewContainerRef to the application ref I need to set any injectable service I'm going to use inside the custom dialog in app.module.ts.

@mseemann
Copy link
Owner

mseemann commented Oct 7, 2016

@grizzm0 I'm not quire sure what your problems are. I have updates the example dialog: http://mseemann.io/angular2-mdl/dialogs. If you checkout the sources https://github.com/mseemann/angular2-mdl/tree/angular2-mdl-1.x/src/app/dialog you'll find a login module - this module is the only thing you need to include in your app module. A full encapsulated piece of code. See for example the LoginService. The service isn't known to the app module.

Setting the defaultViewContainerRef, like it is done in https://github.com/mseemann/angular2-mdl/blob/angular2-mdl-1.x/src/app/app.component.ts#L103 isn't related to the login modul.

@mseemann
Copy link
Owner

mseemann commented Oct 9, 2016

@tb got the declarative dialog working (master). It would be great if you can take a short look if this works as you did expected it to work! There is one open issue: it is only possible to create one instance of such an embedded dialog - but i think this is not a blocker.

@tb
Copy link
Contributor Author

tb commented Oct 10, 2016

@mseemann Great work! I checked it works great!

For my current use cases I have no problem with one instance limitation. Do you think its fixable in future?

@mseemann
Copy link
Owner

@tb thx for your feedback!

I don't know. This seems to be the way a simple TemplateRef works. Just to make it clear: you can only show one instance at once. but if you have closed the dialog the same dialog can be shown again.

closing this issue. need some documentation to do and hopefully release version 2.0 tomorrow.

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

4 participants