{"id":16217392,"url":"https://github.com/sukima/ember-confirmed","last_synced_at":"2025-03-19T10:30:45.569Z","repository":{"id":57223585,"uuid":"102902704","full_name":"sukima/ember-confirmed","owner":"sukima","description":"An Ember asynchronous confirmation addon","archived":false,"fork":false,"pushed_at":"2019-02-13T19:11:20.000Z","size":127,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T17:56:42.672Z","etag":null,"topics":["ember","ember-addon","javascript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sukima.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-09-08T20:40:13.000Z","updated_at":"2024-05-30T03:11:43.000Z","dependencies_parsed_at":"2022-08-30T02:40:17.488Z","dependency_job_id":null,"html_url":"https://github.com/sukima/ember-confirmed","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sukima%2Fember-confirmed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sukima%2Fember-confirmed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sukima%2Fember-confirmed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sukima%2Fember-confirmed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sukima","download_url":"https://codeload.github.com/sukima/ember-confirmed/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243982182,"owners_count":20378605,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ember","ember-addon","javascript"],"created_at":"2024-10-10T11:44:54.439Z","updated_at":"2025-03-19T10:30:45.244Z","avatar_url":"https://github.com/sukima.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ember-confirmed\n\nAn Ember asynchronous confirmation addon\n\nA very simple *promise-like* API for easily showing a confirmation and\nresolving to the result of the user input. Its main goal is to replace or\nenhance the typical `window.confirm` and many adhoc event based solutions (see\nexample below).\n\nSpecial thanks to [FunnelCloud Inc.](http://funnelcloud.io/) for graciously\nproviding inspiration, R+D, and support.\n\nLicense: MIT\n\n## Installation\n\n```\nember install ember-confirmed\n```\n\n## Usage\n\n```\nimport Confirmer from 'confirmer';\n```\n\nMuch like the `Promise` API the constructor for the `Confirmer` takes\na function. That function is passed a resolver object which has four functions\non it that you can use to *fulfill* the `Confirmer`.\n\n```js\nnew Confirmer(function (resolver) {\n  // Affirm confirmation\n  resolver.confirm();\n  // Reject confirmation\n  resolver.reject();\n  // Cancel confirmation\n  resolver.cancel();\n  // Reject with an Error\n  resolver.error(new Error());\n})\n```\n\nEach state can take an optional value. The `Confirmer` is a wrapper around\na `Promise` and can be treated as a promise. For example to capture any errors\nor exceptions you may trigger you would use the `catch()` function.\n\n```js\nnew Confirmer(function (resolver) { … })\n  .catch(function (reason) { console.error(reason); });\n```\n\nThe `then()` function will be passed the underlying data object:\n\n```\nnew Confirmer(function (resolver) { … })\n  .then(function (result) {\n    console.log(result.reason);\n    console.log(result.value);\n  });\n```\n\nThe `reason` being one of `rejected`, `confirmed`, or `cancelled`. And the\n`value` is the value passed to one of the resolver functions.\n\nThe following methods are chainable:\n\n#### `onCanceled`\n\nIs called when `resolver.cancel()` is triggered. Used to denote that the\nconfirmation was cancelled and perhaps should do nothing.\n\n#### `onConfirmed`\n\nIs called when `resolver.confirm()` is triggered. Used to denote that the user\nhas confirmed in some way. (\"OK\" button, correct login credentials, etc.)\n\n#### `onRejected`\n\nIs called when `resolver.rejected()` is triggered. Used to denote that the user\nhas performed an action that denied the confirmation. (\"No\" button, bad\npassword, etc.)\n\n#### `onDone`\n\nIs called when **any** of the resolver functions are triggered. This is used for\nclean up like closing the dialog and removing stale event handlers. This is also\ncalled if the `resolver.error` is triggered or something throws an exception in\nthe initialization function (which can be captued by the `catch()` function just\nlike a promise).\n\n## Examples\n\nThe following are example situations that I've run into and how this module can\nhelp reason about them.\n\n### Basic `window.confirm`\n\nIn this example we will wrap the `window.confirm`. Although this is **not**\nasynchronous it does illustrate the API.\n\n```js\nnew Confirmer(function (resolver) {\n  if (confirm('Whould you like to do this?')) {\n    resolver.confirm();\n  } else {\n    resolver.cancel();\n  }\n})\n  .onConfirmed(function () { console.log('Ok! let\\'s crack on!'); })\n  .onCancelled(function () { console.log('Maybe next time then?'); })\n  .onDone(function () { console.log('Confirm completed') });\n```\n\n### Example component\n\n```hbs\n\u003cp\u003eResult was confirmed: {{if confirmed 'YES' 'NO'}}\u003c/p\u003e\n\n{{#if resolver}}\n  \u003cp\u003eConfirmation?\u003c/p\u003e\n  \u003cbutton onclick={{action resolver.cancel}}\u003eCancel\u003c/button\u003e\n  \u003cbutton onclick={{action resolver.confirm}}\u003eOk\u003c/button\u003e\n{{else}}\n  \u003cbutton onclick={{action \"showDialog\"}}\u003eShow Dialog\u003c/button\u003e\n{{/if}}\n```\n\n```js\nimport Component from '@ember/component';\nimport Confirmer from 'confirmer';\n\nexport default Component.extend({\n  actions: {\n    showDialog() {\n      new Confirmer(resolver =\u003e this.set('resolver', resolver))\n        .onConfirmed(() =\u003e this.set('confirmed', true))\n        .onCancelled(() =\u003e this.set('confirmed', false))\n        .onDone(() =\u003e this.set('resolver', null));\n    }\n  }\n});\n```\n\n### Password prompt\n\nMaybe the resolution of the confirmation needs more logic. For example asking\nfor a password.\n\n```hbs\n\u003cp\u003eResult was confirmed: {{if confirmed 'YES' 'NO'}}\u003c/p\u003e\n\n{{#if resolver}}\n  \u003clabel\u003e\n    Password: {{input type=\"password\" value=password}}\n  \u003c/label\u003e\n  \u003cbutton onclick={{action resolver.cancel}}\u003eCancel\u003c/button\u003e\n  \u003cbutton onclick={{action resolver.confirm password}}\u003eOk\u003c/button\u003e\n{{else}}\n  \u003cbutton onclick={{action \"showDialog\"}}\u003eShow Dialog\u003c/button\u003e\n{{/if}}\n```\n\n```js\nimport Component from '@ember/component';\nimport Confirmer from 'confirmer';\n\nconst REAL_PASSWORD = 'password';\n\nexport default Component.extend({\n  actions: {\n    showDialog() {\n      new Confirmer(resolver =\u003e this.set('resolver', resolver))\n        .onConfirmed(password =\u003e {\n          this.set('confirmed', password === REAL_PASSWORD);\n        })\n        .onCancelled(() =\u003e this.set('confirmed', false))\n        .onDone(() =\u003e this.set('resolver', null));\n    }\n  }\n});\n```\n\n### Auto closing message box\n\nHere is an example of a message box that auto closes after 5 seconds.\n\nNotice that you can call the resolver functions multiple times and only the\nfirst one wins.\n\n```js\nimport Component from '@ember/component';\nimport { later } from '@ember/runloop';\nimport Confirmer from 'confirmer';\n\nconst DIALOG_AUTO_CLOSE_DELAY = 5000;\n\nexport default Component.extend({\n  actions: {\n    showDialog() {\n      new Confirmer(resolver =\u003e {\n        later(resolver.cancel, DIALOG_AUTO_CLOSE_DELAY);\n        this.set('resolver', resolver);\n      })\n        .onConfirmed(() =\u003e this.set('confirmed', true))\n        .onCancelled(() =\u003e this.set('confirmed', false))\n        .onDone(() =\u003e this.set('resolver', null));\n    }\n  }\n});\n```\n\n### Unsaved changes confirmation on route transition\n\nHere is an example if trapping a route transition and showing a confirmation\ndialog if the data is not saved (dirty).\n\nIt shows how you can pass a Confirmer object around between each other.\n\n#### `app/routes/index.js`\n\n```js\nimport Route from '@ember/routing/route';\n\nexport default Route.extend({\n  actions: {\n    willTransition(transition) {\n      let confirmer = this.controller.showDirtyConfirmation();\n      if (confirmer) {\n        transition.abort();\n        confirmer.onConfirmed(() =\u003e transition.retry());\n      }\n    }\n  }\n});\n```\n\n#### `app/controllers/index.js`\n\n```js\nimport Controller from '@ember/controller';\nimport Confirmer from 'confirmer';\n\nexport default Controller.extend({\n  showDirtyConfirmation() {\n    if (!this.get('model.hasDirtyAttributes')) { return; }\n    return new Confirmer(resolver =\u003e {\n      this.set('modalAction', resolver);\n      this.set('showConfirmationModal', true);\n    })\n      .onConfirmed(() =\u003e this.get('model').rollbackAttributes())\n      .onDone(() =\u003e this.set('showConfirmationModal', false));\n  }\n});\n```\n\n#### `app/templates/index.hbs`\n\n```hbs\n{{#if showConfirmationModal}}\n  \u003cdiv class=\"modal\"\u003e\n    \u003cp\u003eYou have unsaved changes. Are you sure wish to abandon them?\u003cp\u003e\n    \u003cbutton {{action (action modalActions.cancel)}}\u003eNo\u003c/button\u003e\n    \u003cbutton {{action (action modalActions.confirm)}}\u003eYes\u003c/button\u003e\n  \u003c/div\u003e\n{{/if}}\n```\n\n### Example using ember-remodal\n\nWe have an `{{#ember-remodal-redux}}` component to handle the complexity of\nusing remodal as a component instead of a global/service. If this does not fit\nyour needs then you can still use remodal manually.\n\nThis is an example using the [ember-remodal addon](http://sethbrasile.github.io/ember-remodal/) on your own.\n\n```js\nimport Controller from '@ember/controller';\nimport { inject as service } from '@ember/service';\nimport Confirmer from 'confirmer';\n\nexport default Controller.extend({\n  remodal: service(),\n\n  showConfirmationModal() {\n    const remodal = this.get('remodal');\n    let modalName = 'confirmation'; // Change as needed\n    return new Confirmer(resolver =\u003e {\n      this.set('modalResolver', resolver);\n      let promise = remodal.open(modalName);\n      resolver.dispose(() =\u003e {\n        // https://github.com/sethbrasile/ember-remodal/issues/3\n        return promise\n          .then(() =\u003e {\n            let modalState = tryInvoke(this.get('modal'), 'getState');\n            // https://github.com/vodkabears/Remodal/issues/291\n            if (modalState !== 'opened') { return; }\n            return remodal.close(modalName);\n          })\n          .then(() =\u003e this.set('modalResolver', null));\n      });\n    });\n  },\n\n  actions: {\n    resolveModal(method, value) {\n      this.get('modalResolver')[method](value);\n    }\n  }\n});\n```\n\n```hbs\n{{#ember-remodal\n    forService=true\n    name=\"confirmation\"\n    onConfirm=(action \"resolveModal\" \"confirm\")\n    onCancel=(action \"resolveModal\" \"reject\")\n    onClose=(action \"resolveModal\" \"cancel\")\n    as |modal|}}\n  {{#modal.cancel}}\u003cbutton class=\"pull-right\"\u003eClose\u003c/button\u003e{{/modal.cancel}}\n  \u003cp\u003ePick yes or no:\u003c/p\u003e\n  {{#modal.cancel}}\u003cbutton\u003eNo\u003c/button\u003e{{/modal.cancel}}\n  {{#modal.confirm}}\u003cbutton\u003eYes\u003c/button\u003e{{/modal.confirm}}\n{{/ember-remodal}}\n```\n\n### Example using ember-sweetalert\n\nThis is an example of wrapping the [ember-sweetalert addon](https://github.com/Tonkpils/ember-sweetalert) in a Confirmer object.\n\n```js\nimport Confirmer from 'confirmer';\nimport sweetAlert from 'ember-sweetalert';\n\nexport default function sweetAlertConfirmer(sweetAlertOptions) {\n  return new Confirmer(resolver =\u003e {\n    sweetAlert(sweetAlertOptions)\n      .then(result =\u003e {\n        if (result.dismiss) {\n          resolver.cancel(result.dismiss);\n        } else {\n          resolver.confirm(result.value);\n        }\n      })\n      .catch(resolver.error);\n  });\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsukima%2Fember-confirmed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsukima%2Fember-confirmed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsukima%2Fember-confirmed/lists"}