material-components / material-web

Material Design Web Components
https://material-web.dev
Apache License 2.0
9.31k stars 890 forks source link

Show Dialogs and Snackbars via function call #1744

Closed LasseRosenow closed 4 years ago

LasseRosenow commented 4 years ago

Showing a Snackbar or a Dialog is over complicated with material-components. You have to define the html somewhere for each dialog and each Snackbar and have to geht them by their ID to show them.

Describe the solution you'd like It would be better to be able to create a Snackbar or a Alertdialog via a function call like other frameworks do:

Ionic components:

// Dialog
const alert = await alertController.create({
   cssClass: 'my-custom-class',
   header: 'Alert',
   subHeader: 'Subtitle',
   message: 'This is an alert message.',
   buttons: ['OK']
});
await alert.present();

// Snackbar
const toast = await toastController.create({
  message: 'Your settings have been saved.',
  duration: 2000
});
toast.present();

Flutter:

// Dialog
showDialog<void>(
    context: context,
    barrierDismissible: false, // user must tap button!
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text('AlertDialog Title'),
        content: Text('Content Text'),
      ),
      actions: <Widget>[
        FlatButton(
          child: Text('Approve'),
          onPressed: () {
            Navigator.of(context).pop();
          },
        ),
      ],
    );
  },
);

// Snackbar
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);

Angular Material:

// Dialog
const dialogRef = this.dialog.open(DialogOverviewExampleDialog, {
    width: '250px',
    data: {name: this.name, animal: this.animal}
});

// Snackbar
this._snackBar.open(message, action, {
    duration: 2000,
});
dfreedm commented 4 years ago

I think an important issue here is that these APIs in your examples are all framework-specific APIs, each with their own tradeoffs and expectations, and Material Web Components are not tied to usage in any particular framework. Particularly with something as complicated as Dialog, it is easy to build an API that doesn't cover enough use cases, or is a leaky abstraction that can be more trouble to use than it is worth. This makes us very hesitant to add APIs like this to Material Web Components.

However, because a builder-style API is just effectively a wrapper around building DOM, it is very easy for you to build something that works exactly how you want, without having to write the HTML by hand.

For example, a simple snackbar API could be

async function showSnackbar(message: string, duration: number, acceptText = 'ok', cancelText = 'cancel'): Promise<boolean> {
  const snackbar = document.createElement('mwc-snackbar');
  snackbar.label = message;
  snackbar.timeoutMs = duration;
  if (acceptText) {
    const button = document.createElement('mwc-button');
    button.label = acceptText;
    button.slot = 'action';
    snackbar.appendChild(button);
  }
  if (cancelText) {
    const button = document.createElement('mwc-button');
    button.label = cancelText;
    button.slot = 'cancel';
    snackbar.appendChild(button);
  }
  document.body.appendChild(snackbar);
  let resolve: (value?: boolean) => void;
  const reasonPromise = new Promise<boolean>((res) => {resolve = res});
  snackbar.addEventListener('MDCSnackbar:closed', (event) => {
    resolve(event?.detail?.reason === 'action' ?? false)
  });
  snackbar.open();
  const acceptOrReject = await reasonPromise;
  snackbar.remove();
  return acceptOrReject
}
brownieboy commented 3 years ago

@dfreedm,

I think your if (acceptText) { and if (cancelText) { tests will always pass because you're setting defaults on those two parameters when they're passed into your function.