https://github.com/sukima/ember-confirmed
An Ember asynchronous confirmation addon
https://github.com/sukima/ember-confirmed
ember ember-addon javascript
Last synced: 8 months ago
JSON representation
An Ember asynchronous confirmation addon
- Host: GitHub
- URL: https://github.com/sukima/ember-confirmed
- Owner: sukima
- License: other
- Created: 2017-09-08T20:40:13.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2019-02-13T19:11:20.000Z (over 6 years ago)
- Last Synced: 2025-02-28T17:56:42.672Z (8 months ago)
- Topics: ember, ember-addon, javascript
- Language: JavaScript
- Homepage:
- Size: 124 KB
- Stars: 12
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# 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.](http://funnelcloud.io/) for graciously
providing inspiration, R+D, and support.
License: MIT
## Installation
```
ember install ember-confirmed
```
## Usage
```
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`.
```js
new Confirmer(function (resolver) {
// Affirm confirmation
resolver.confirm();
// Reject confirmation
resolver.reject();
// Cancel confirmation
resolver.cancel();
// Reject with an Error
resolver.error(new Error());
})
```
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.
```js
new Confirmer(function (resolver) { … })
.catch(function (reason) { console.error(reason); });
```
The `then()` function will be passed the underlying data object:
```
new Confirmer(function (resolver) { … })
.then(function (result) {
console.log(result.reason);
console.log(result.value);
});
```
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.
```js
new Confirmer(function (resolver) {
if (confirm('Whould you like to do this?')) {
resolver.confirm();
} else {
resolver.cancel();
}
})
.onConfirmed(function () { console.log('Ok! let\'s crack on!'); })
.onCancelled(function () { console.log('Maybe next time then?'); })
.onDone(function () { console.log('Confirm completed') });
```
### Example component
```hbs
Result was confirmed: {{if confirmed 'YES' 'NO'}}
{{#if resolver}}
Confirmation?
Cancel
Ok
{{else}}
Show Dialog
{{/if}}
```
```js
import Component from '@ember/component';
import Confirmer from 'confirmer';
export default Component.extend({
actions: {
showDialog() {
new Confirmer(resolver => this.set('resolver', resolver))
.onConfirmed(() => this.set('confirmed', true))
.onCancelled(() => this.set('confirmed', false))
.onDone(() => this.set('resolver', null));
}
}
});
```
### Password prompt
Maybe the resolution of the confirmation needs more logic. For example asking
for a password.
```hbs
Result was confirmed: {{if confirmed 'YES' 'NO'}}
{{#if resolver}}
Password: {{input type="password" value=password}}
Cancel
Ok
{{else}}
Show Dialog
{{/if}}
```
```js
import Component from '@ember/component';
import Confirmer from 'confirmer';
const REAL_PASSWORD = 'password';
export default Component.extend({
actions: {
showDialog() {
new Confirmer(resolver => this.set('resolver', resolver))
.onConfirmed(password => {
this.set('confirmed', password === REAL_PASSWORD);
})
.onCancelled(() => this.set('confirmed', false))
.onDone(() => this.set('resolver', null));
}
}
});
```
### 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.
```js
import Component from '@ember/component';
import { later } from '@ember/runloop';
import Confirmer from 'confirmer';
const DIALOG_AUTO_CLOSE_DELAY = 5000;
export default Component.extend({
actions: {
showDialog() {
new Confirmer(resolver => {
later(resolver.cancel, DIALOG_AUTO_CLOSE_DELAY);
this.set('resolver', resolver);
})
.onConfirmed(() => this.set('confirmed', true))
.onCancelled(() => this.set('confirmed', false))
.onDone(() => this.set('resolver', null));
}
}
});
```
### 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`
```js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
willTransition(transition) {
let confirmer = this.controller.showDirtyConfirmation();
if (confirmer) {
transition.abort();
confirmer.onConfirmed(() => transition.retry());
}
}
}
});
```
#### `app/controllers/index.js`
```js
import Controller from '@ember/controller';
import Confirmer from 'confirmer';
export default Controller.extend({
showDirtyConfirmation() {
if (!this.get('model.hasDirtyAttributes')) { return; }
return new Confirmer(resolver => {
this.set('modalAction', resolver);
this.set('showConfirmationModal', true);
})
.onConfirmed(() => this.get('model').rollbackAttributes())
.onDone(() => this.set('showConfirmationModal', false));
}
});
```
#### `app/templates/index.hbs`
```hbs
{{#if showConfirmationModal}}
You have unsaved changes. Are you sure wish to abandon them?
No
Yes
{{/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](http://sethbrasile.github.io/ember-remodal/) on your own.
```js
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import Confirmer from 'confirmer';
export default Controller.extend({
remodal: service(),
showConfirmationModal() {
const remodal = this.get('remodal');
let modalName = 'confirmation'; // Change as needed
return new Confirmer(resolver => {
this.set('modalResolver', resolver);
let promise = remodal.open(modalName);
resolver.dispose(() => {
// https://github.com/sethbrasile/ember-remodal/issues/3
return promise
.then(() => {
let modalState = tryInvoke(this.get('modal'), 'getState');
// https://github.com/vodkabears/Remodal/issues/291
if (modalState !== 'opened') { return; }
return remodal.close(modalName);
})
.then(() => this.set('modalResolver', null));
});
});
},
actions: {
resolveModal(method, value) {
this.get('modalResolver')[method](value);
}
}
});
```
```hbs
{{#ember-remodal
forService=true
name="confirmation"
onConfirm=(action "resolveModal" "confirm")
onCancel=(action "resolveModal" "reject")
onClose=(action "resolveModal" "cancel")
as |modal|}}
{{#modal.cancel}}Close{{/modal.cancel}}
Pick yes or no:
{{#modal.cancel}}No{{/modal.cancel}}
{{#modal.confirm}}Yes{{/modal.confirm}}
{{/ember-remodal}}
```
### Example using ember-sweetalert
This is an example of wrapping the [ember-sweetalert addon](https://github.com/Tonkpils/ember-sweetalert) in a Confirmer object.
```js
import Confirmer from 'confirmer';
import sweetAlert from 'ember-sweetalert';
export default function sweetAlertConfirmer(sweetAlertOptions) {
return new Confirmer(resolver => {
sweetAlert(sweetAlertOptions)
.then(result => {
if (result.dismiss) {
resolver.cancel(result.dismiss);
} else {
resolver.confirm(result.value);
}
})
.catch(resolver.error);
});
}
```