项目作者: sukima

项目描述 :
An Ember asynchronous confirmation addon
高级语言: JavaScript
项目地址: git://github.com/sukima/ember-confirmed.git
创建时间: 2017-09-08T20:40:13Z
项目社区:https://github.com/sukima/ember-confirmed

开源协议:Other

下载


ember-confirmed

An Ember asynchronous confirmation addon

A very simple promise-like API for easily showing a confirmation and
resolving to the result of the user input. Its main goal is to replace or
enhance the typical window.confirm and many adhoc event based solutions (see
example below).

Special thanks to FunnelCloud Inc. for graciously
providing inspiration, R+D, and support.

License: MIT

Installation

  1. ember install ember-confirmed

Usage

  1. import Confirmer from 'confirmer';

Much like the Promise API the constructor for the Confirmer takes
a function. That function is passed a resolver object which has four functions
on it that you can use to fulfill the Confirmer.

  1. new Confirmer(function (resolver) {
  2. // Affirm confirmation
  3. resolver.confirm();
  4. // Reject confirmation
  5. resolver.reject();
  6. // Cancel confirmation
  7. resolver.cancel();
  8. // Reject with an Error
  9. resolver.error(new Error());
  10. })

Each state can take an optional value. The Confirmer is a wrapper around
a Promise and can be treated as a promise. For example to capture any errors
or exceptions you may trigger you would use the catch() function.

  1. new Confirmer(function (resolver) { })
  2. .catch(function (reason) { console.error(reason); });

The then() function will be passed the underlying data object:

  1. new Confirmer(function (resolver) { })
  2. .then(function (result) {
  3. console.log(result.reason);
  4. console.log(result.value);
  5. });

The reason being one of rejected, confirmed, or cancelled. And the
value is the value passed to one of the resolver functions.

The following methods are chainable:

onCanceled

Is called when resolver.cancel() is triggered. Used to denote that the
confirmation was cancelled and perhaps should do nothing.

onConfirmed

Is called when resolver.confirm() is triggered. Used to denote that the user
has confirmed in some way. (“OK” button, correct login credentials, etc.)

onRejected

Is called when resolver.rejected() is triggered. Used to denote that the user
has performed an action that denied the confirmation. (“No” button, bad
password, etc.)

onDone

Is called when any of the resolver functions are triggered. This is used for
clean up like closing the dialog and removing stale event handlers. This is also
called if the resolver.error is triggered or something throws an exception in
the initialization function (which can be captued by the catch() function just
like a promise).

Examples

The following are example situations that I’ve run into and how this module can
help reason about them.

Basic window.confirm

In this example we will wrap the window.confirm. Although this is not
asynchronous it does illustrate the API.

  1. new Confirmer(function (resolver) {
  2. if (confirm('Whould you like to do this?')) {
  3. resolver.confirm();
  4. } else {
  5. resolver.cancel();
  6. }
  7. })
  8. .onConfirmed(function () { console.log('Ok! let\'s crack on!'); })
  9. .onCancelled(function () { console.log('Maybe next time then?'); })
  10. .onDone(function () { console.log('Confirm completed') });

Example component

  1. <p>Result was confirmed: {{if confirmed 'YES' 'NO'}}</p>
  2. {{#if resolver}}
  3. <p>Confirmation?</p>
  4. <button onclick={{action resolver.cancel}}>Cancel</button>
  5. <button onclick={{action resolver.confirm}}>Ok</button>
  6. {{else}}
  7. <button onclick={{action "showDialog"}}>Show Dialog</button>
  8. {{/if}}
  1. import Component from '@ember/component';
  2. import Confirmer from 'confirmer';
  3. export default Component.extend({
  4. actions: {
  5. showDialog() {
  6. new Confirmer(resolver => this.set('resolver', resolver))
  7. .onConfirmed(() => this.set('confirmed', true))
  8. .onCancelled(() => this.set('confirmed', false))
  9. .onDone(() => this.set('resolver', null));
  10. }
  11. }
  12. });

Password prompt

Maybe the resolution of the confirmation needs more logic. For example asking
for a password.

  1. <p>Result was confirmed: {{if confirmed 'YES' 'NO'}}</p>
  2. {{#if resolver}}
  3. <label>
  4. Password: {{input type="password" value=password}}
  5. </label>
  6. <button onclick={{action resolver.cancel}}>Cancel</button>
  7. <button onclick={{action resolver.confirm password}}>Ok</button>
  8. {{else}}
  9. <button onclick={{action "showDialog"}}>Show Dialog</button>
  10. {{/if}}
  1. import Component from '@ember/component';
  2. import Confirmer from 'confirmer';
  3. const REAL_PASSWORD = 'password';
  4. export default Component.extend({
  5. actions: {
  6. showDialog() {
  7. new Confirmer(resolver => this.set('resolver', resolver))
  8. .onConfirmed(password => {
  9. this.set('confirmed', password === REAL_PASSWORD);
  10. })
  11. .onCancelled(() => this.set('confirmed', false))
  12. .onDone(() => this.set('resolver', null));
  13. }
  14. }
  15. });

Auto closing message box

Here is an example of a message box that auto closes after 5 seconds.

Notice that you can call the resolver functions multiple times and only the
first one wins.

  1. import Component from '@ember/component';
  2. import { later } from '@ember/runloop';
  3. import Confirmer from 'confirmer';
  4. const DIALOG_AUTO_CLOSE_DELAY = 5000;
  5. export default Component.extend({
  6. actions: {
  7. showDialog() {
  8. new Confirmer(resolver => {
  9. later(resolver.cancel, DIALOG_AUTO_CLOSE_DELAY);
  10. this.set('resolver', resolver);
  11. })
  12. .onConfirmed(() => this.set('confirmed', true))
  13. .onCancelled(() => this.set('confirmed', false))
  14. .onDone(() => this.set('resolver', null));
  15. }
  16. }
  17. });

Unsaved changes confirmation on route transition

Here is an example if trapping a route transition and showing a confirmation
dialog if the data is not saved (dirty).

It shows how you can pass a Confirmer object around between each other.

app/routes/index.js

  1. import Route from '@ember/routing/route';
  2. export default Route.extend({
  3. actions: {
  4. willTransition(transition) {
  5. let confirmer = this.controller.showDirtyConfirmation();
  6. if (confirmer) {
  7. transition.abort();
  8. confirmer.onConfirmed(() => transition.retry());
  9. }
  10. }
  11. }
  12. });

app/controllers/index.js

  1. import Controller from '@ember/controller';
  2. import Confirmer from 'confirmer';
  3. export default Controller.extend({
  4. showDirtyConfirmation() {
  5. if (!this.get('model.hasDirtyAttributes')) { return; }
  6. return new Confirmer(resolver => {
  7. this.set('modalAction', resolver);
  8. this.set('showConfirmationModal', true);
  9. })
  10. .onConfirmed(() => this.get('model').rollbackAttributes())
  11. .onDone(() => this.set('showConfirmationModal', false));
  12. }
  13. });

app/templates/index.hbs

  1. {{#if showConfirmationModal}}
  2. <div class="modal">
  3. <p>You have unsaved changes. Are you sure wish to abandon them?<p>
  4. <button {{action (action modalActions.cancel)}}>No</button>
  5. <button {{action (action modalActions.confirm)}}>Yes</button>
  6. </div>
  7. {{/if}}

Example using ember-remodal

We have an {{#ember-remodal-redux}} component to handle the complexity of
using remodal as a component instead of a global/service. If this does not fit
your needs then you can still use remodal manually.

This is an example using the ember-remodal addon on your own.

  1. import Controller from '@ember/controller';
  2. import { inject as service } from '@ember/service';
  3. import Confirmer from 'confirmer';
  4. export default Controller.extend({
  5. remodal: service(),
  6. showConfirmationModal() {
  7. const remodal = this.get('remodal');
  8. let modalName = 'confirmation'; // Change as needed
  9. return new Confirmer(resolver => {
  10. this.set('modalResolver', resolver);
  11. let promise = remodal.open(modalName);
  12. resolver.dispose(() => {
  13. // https://github.com/sethbrasile/ember-remodal/issues/3
  14. return promise
  15. .then(() => {
  16. let modalState = tryInvoke(this.get('modal'), 'getState');
  17. // https://github.com/vodkabears/Remodal/issues/291
  18. if (modalState !== 'opened') { return; }
  19. return remodal.close(modalName);
  20. })
  21. .then(() => this.set('modalResolver', null));
  22. });
  23. });
  24. },
  25. actions: {
  26. resolveModal(method, value) {
  27. this.get('modalResolver')[method](value);
  28. }
  29. }
  30. });
  1. {{#ember-remodal
  2. forService=true
  3. name="confirmation"
  4. onConfirm=(action "resolveModal" "confirm")
  5. onCancel=(action "resolveModal" "reject")
  6. onClose=(action "resolveModal" "cancel")
  7. as |modal|}}
  8. {{#modal.cancel}}<button class="pull-right">Close</button>{{/modal.cancel}}
  9. <p>Pick yes or no:</p>
  10. {{#modal.cancel}}<button>No</button>{{/modal.cancel}}
  11. {{#modal.confirm}}<button>Yes</button>{{/modal.confirm}}
  12. {{/ember-remodal}}

Example using ember-sweetalert

This is an example of wrapping the ember-sweetalert addon in a Confirmer object.

  1. import Confirmer from 'confirmer';
  2. import sweetAlert from 'ember-sweetalert';
  3. export default function sweetAlertConfirmer(sweetAlertOptions) {
  4. return new Confirmer(resolver => {
  5. sweetAlert(sweetAlertOptions)
  6. .then(result => {
  7. if (result.dismiss) {
  8. resolver.cancel(result.dismiss);
  9. } else {
  10. resolver.confirm(result.value);
  11. }
  12. })
  13. .catch(resolver.error);
  14. });
  15. }