{"id":15015828,"url":"https://github.com/adopted-ember-addons/emberx-select","last_synced_at":"2026-03-08T16:32:28.643Z","repository":{"id":27402794,"uuid":"30879325","full_name":"adopted-ember-addons/emberx-select","owner":"adopted-ember-addons","description":"Select component for Ember based on the native html select element. ","archived":false,"fork":false,"pushed_at":"2023-11-28T02:39:37.000Z","size":1159,"stargazers_count":198,"open_issues_count":31,"forks_count":80,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-08-20T13:24:06.033Z","etag":null,"topics":["ember","ember-addon","emberx-select","javascript","select"],"latest_commit_sha":null,"homepage":"https://emberx-select.netlify.com/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adopted-ember-addons.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-02-16T17:25:43.000Z","updated_at":"2025-07-07T22:45:33.000Z","dependencies_parsed_at":"2024-06-18T13:56:39.936Z","dependency_job_id":null,"html_url":"https://github.com/adopted-ember-addons/emberx-select","commit_stats":null,"previous_names":["thefrontside/emberx-select"],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/adopted-ember-addons/emberx-select","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Femberx-select","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Femberx-select/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Femberx-select/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Femberx-select/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adopted-ember-addons","download_url":"https://codeload.github.com/adopted-ember-addons/emberx-select/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Femberx-select/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280943388,"owners_count":26417747,"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","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","emberx-select","javascript","select"],"created_at":"2024-09-24T19:48:00.692Z","updated_at":"2025-10-25T16:13:51.204Z","avatar_url":"https://github.com/adopted-ember-addons.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# emberx-select\n[![npm version](https://badge.fury.io/js/emberx-select.svg)](http://badge.fury.io/js/emberx-select)\n[![Ember Observer Score](http://emberobserver.com/badges/emberx-select.svg)](http://emberobserver.com/addons/emberx-select)\n[![CircleCI](https://circleci.com/gh/thefrontside/emberx-select/tree/master.svg?style=svg)](https://circleci.com/gh/thefrontside/emberx-select/tree/master)\n\nA select component based on the native html select.\n\nWe've tried other select components, and were missing the reliability,\nmaintainability, and accessbility of the native html `\u003cselect\u003e`.\n`\u003cXSelect\u003e` is a drop-in component to let you use any\nobject for your selectable options. You can use it out of the box, or\nas a building block of something more ambitious.\n\nThe goal of `\u003cXSelect\u003e` is to let you see how it works and style it\nright in your template, rather than passing in a ball of configuration\nor wrapping a hard-coded, inaccessible jQuery plugin.\n\n## Recommended \u003cSelectLight\u003e\n\nThis addon contains older Ember patterns and depdendencies.\nWhile it is still ok to continue using, it is recommended to use the more modern\n[Ember Select Light](https://github.com/ember-a11y/ember-select-light/) addon which has a near drop in API and better support for Octane, Embroider, and Accessibility concerns.\n\n## Installation\n\n```\nember install emberx-select\n```\n\n## Usage\n\nBy allowing arbitrary html to appear in the template of the select\nelement, you can use it just like you would normally. This means\nthings like having `\u003coptgroup\u003e` tags inside your select, or even plain\nold `\u003coption\u003e` elements to represent things like empty values.\n\n`\u003cXSelect\u003e` thinly wraps a native `\u003cselect\u003e` element so that it can be object\nand binding aware. It is used in conjuction with the `x-option`\ncomponent to construct select boxes. E.g.\n\n**Ember \u003e= 3.4:**\n```handlebars\n\u003cXSelect @value={{bob}} @onChange={{action \"selectPerson\"}} as |xs|\u003e\n  \u003cxs.option @value={{fred}}\u003eFred Flintstone\u003c/xs.option\u003e\n  \u003cxs.option @value={{bob}}\u003eBob Newhart\u003c/xs.option\u003e\n\u003c/XSelect\u003e\n```\n\n**Ember \u003c 3.4:**\n```handlebars\n{{#x-select value=bob on-change=(action \"selectPerson\") as |xs|}}\n  {{#xs.option value=fred}}Fred Flintstone{{/xs.option}}\n  {{#xs.option value=bob}}Bob Newhart{{/xs.option}}\n{{/x-select}}\n```\n\nThe options are always up to date, so that when the object bound to\n`value` changes, the corresponding option becomes selected.\n\nWhenever the select tag receives a change event, it will fire\n`onChange` action. This is the default action that is fired but not\nthe only event that's available.\n\n\n### Contextual Components\n\nAs of version 3.0.0, `emberx-select` will only support contextual\ncomponents. This means you will have to use Ember 2.3 or higher. Using\ncontextual components allows `emberx-select` to skip some\npotentially expensive DOM traversals. Now the options can register\nthrough data rather than through the DOM.\n\n```handlebars\n\u003cXSelect @value={{model.status}} as |xs|\u003e\n  \u003cxs.option @value=1\u003eActive\u003c/xs.option\u003e\n  \u003cxs.option @value=2\u003eInactive\u003c/xs.option\u003e\n\u003c/XSelect\u003e\n```\n\n### Multiselect\n\n`\u003cXSelect\u003e` supports the `multiple` option. This means you can pass an\narray as its value, and it will set its selections directly on that\narray.\n\n```handlebars\n\u003cXSelect @value=selections @multiple=true @onChange={{action \"selectionsChanged\"}} as |xs|\u003e\n \u003cxs.option @value={{fred}}\u003eFred Flintstone\u003c/xs.option\u003e\n \u003cxs.option @value={{bob}}\u003eBob Newhart\u003c/xs.option\u003e\n \u003cxs.option @value={{andrew}}\u003eAndrew WK\u003c/xs.option\u003e\n\u003c/XSelect\u003e\n```\n\nThe selections array will be initialized to an empty array if not present.\n\n## Actions and Action Arguments\n\nAll of `\u003cXSelect\u003e`s actions are closure actions. This means you must use\nthe `action` helper (i.e. `@onClick={{action \"onClick\"}}`). The function\nthat is dispatched by `\u003cXSelect\u003e` whenever the event fires has a function\nsignature of:\n\n```js\n/**\n* @param {Object} value - the value selected by the user.\n* @param {Object} event - the DOM event of the action\n*/\nfunction (value, event) {\n  // action body...\n}\n```\n\nMost of the time all you need is the value that has been selected, but\nsometimes your action requires more context than just that. In those\ncases, you can pass any arguments you need from the template. For\nexample:\n\n```handlebars\n\u003cXSelect @onClick={{action \"didMakeSelection\" isXSelectRequired}} @required={{isXSelectRequired}} as |xs|\u003e\n  \u003coption\u003eNothing\u003c/option\u003e\n  \u003cxs.option @value={{something}}\u003eSomething\u003c/xs.option\u003e\n\u003c/XSelect\u003e\n```\n\nthen, inside your action handler:\n\n```js\nimport Controller from '@ember/controller';\n\nexport default Controller.extend({\n  actions: {\n    didMakeSelection(value, event, isXSelectRequired) {\n      if (!value \u0026 isXSelectRequired) {\n        this.set('error', 'You must fill out this field');\n      } else {\n        this.set('selection', value);\n      }\n    }\n  }\n});\n```\n\n`\u003cXSelect\u003e` provides other actions that fire on different event\ntypes. These actions follow the HTML input event naming convention.\n\n**onBlur**\n\n`onBlur` fires anytime the `blur` event is triggered on the `\u003cXSelect\u003e`\ncomponent. When the action fires it sends two arguments: the value,\nthe DOM event.\n\n**onFocusOut**\n\n`onFocusOut` fires anytime the `focusOut` event is triggered on the `\u003cXSelect\u003e`\ncomponent. When the action fires it sends two arguments: the value,\nthe DOM event.\n\n**onClick**\n\n`onClick` fires when `\u003cXSelect\u003e` is clicked. When the action fires it\nsends two arguments: the value, the DOM event.\n\n**onDisable** (x-option)\n\n`onDisable` fires when x-option detects a change to its `disabled`\nattribute. When the action fires it sends two arguments: the value\nand if it is disabled (boolean).\n\n### Test Helper\n\n`\u003cXSelect\u003e` 4.0 ships with an entirely new test helper that goes\nbeyond just allowing you to select an option. It allows you to\ninteract with your `\u003cselect\u003e` element in all different ways. For\nexample, if you need to assert your first option is `disabled`\nor not:\n\n```javascript\nexpect(xselect.options(0).isDisabled).to.equal(true);\n```\n\nUnder the hood this new test helper is using a [BigTest\nInteractor](https://bigtestjs.io/guides/interactors/introduction/). Interactors\nallow you to think about how you're going to _interact_ with the DOM\nand abstract that into composable \u0026 immutable containers. Interactors\nare similar to page objects, but for components.\n\n#### Using the test helper\n\nImport the select interactor:\n\n``` javascript\n// you can name the import whatever you want\nimport XSelectInteractor from 'emberx-select/test-support/interactor';\n```\n\nAt the top of your test file you need to initialize the\ninteractor. This should go at the top most part of your test so it's\navailable to all tests in the file. Here's an example in Qunit:\n\n\n``` javascript\nmodule(\"Acceptance | Your Test\", function(hooks) {\n  let xselect = new XSelectInteractor('.selector-for-select');\n  setupApplicationTest(hooks);\n  // ...\n});\n```\n\nOnce you have initialized the interactor, you're ready to start\nselecting!\n\n``` javascript\nmodule(\"Acceptance | Your Test\", function(hooks) {\n  let xselect = new XSelectInteractor('.selector-for-select');\n  // ...\n\n  test('Selecting an option', async (assert) =\u003e {\n    await xselect\n      .select('Fred Flintstone')\n      .when(() =\u003e assert.equal(xselect.options(0).isSelected, true));\n\n    // for a multiselect pass an array\n    // await xselect\n    //   .select(['Fred Flintstone', 'Bob Newhart'])\n    //   .when(() =\u003e assert.equal(xselect.options(0).isSelected, true));;\n  });\n});\n```\n\nYou can do more than just select options with this helper.\n\n``` javascript\nmodule('Acceptance | Your Test', function(hooks) {\n  let xselect = new XSelectInteractor('.selector-for-select');\n  // ...\n\n  test('Selecting an option', async (assert) =\u003e {\n    await xselect.select('Fred Flintstone')\n      // assert the change is has happened. It's important to make the\n      // assertion inside of `when`, so tests are not flakey.\n      .when(() =\u003e assert.equal(xselect.options(0).isSelected, true));\n  });\n});\n```\n\nIn this example we're using `@bigtest/convergence#when` to\nassert. The TL;DR of convergence is it basically _converges_ on the\nstate of the DOM. It checks every 10ms until the assertion is\ntruthy. Once it's truthy the test passes. [You can read more about\nconvergences here](https://github.com/bigtestjs/convergence#why-convergence)\n\nYou don't need to include `@bigtest/convergence` in your project, it's\nalready a dependency of `@bigtest/interactor` and interactor provides\nall of the convergence methods to you (like `when` and `do`).\n\nThis is the full interactor which has all of the attributes or\ninteractions for an `HTMLSelectElement`.\n\n``` javascript\nconst xSelectInteractor = interactor({\n  hasFocus: is(':focus'),\n  name: attribute('name'),\n  form: attribute('form'),\n  title: attribute('title'),\n  size: attribute('size'),\n  tabindex: attribute('tabindex'),\n  isDisabled: property('disabled'),\n  isRequired: property('required'),\n  isAutofocus: property('autofocus'),\n\n  options: collection('option', {\n    name: attribute('name'),\n    value: property('value'),\n    title: attribute('title'),\n    isSelected: property('selected'),\n    isDisabled: property('disabled'),\n    hasSelectedClass: hasClass('is-selected')\n  })\n});\n```\n\nExample usage might be:\n\n``` html\n\u003cselect name=\"World\" class=\"x-select\"\u003e\n  \u003coption value=\"hello world\"\u003eHello world!\u003c/option\u003e\n\u003c/select\u003e\n```\n\n``` javascript\nlet xselect = new XSelectInteractor('.x-select');\n\nxselect.options(0).value; //=\u003e \"hello world\"\nxselect.options(0).text; //=\u003e \"Hello World!\"\nxselect.name; //=\u003e \"World\"\nxselect.form; //=\u003e null\nxselect.hasFocus; //=\u003e false\nxselect.tabIndex; //=\u003e 0\n```\n\nIf you want to see this test helper used in many different ways look\nno further than [this addons test suite!](https://github.com/thefrontside/emberx-select/tree/master/tests)\n\n#### Extending the XSelect interactor\n\nIf you want to add custom interactions to your `\u003cXSelect\u003e` interactor,\nyou can do so by importing it into the custom interactor you want to\ncreate, and extend it:\n\n``` javascript\nimport XSelectInteractor from 'emberx-select/test-support/interactor';\nimport { clickable } from '@bigtest/interactor';\n\n@XSelectInteractor.extend\nclass NewInteractor {\n  submitForm = clickable('[data-test-form-submit]');\n\n  fillAndSubmit(value) {\n    return this.select(value).submitForm();\n  }\n}\n```\n\n## EmberX\n\nemberx-select is part of the \"missing components of ember\" collectively\nknown as emberx:\n\n* [emberx-select](https://github.com/thefrontside/emberx-select)\n* [emberx-slider](https://github.com/thefrontside/emberx-slider)\n* [emberx-file-input](https://github.com/thefrontside/emberx-file-input)\n\n## Other Resources\n\n* [EmberScreencasts video on creating select boxes with vanilla Ember and emberx-select](https://www.emberscreencasts.com/posts/54-select-boxes-in-ember-20)\n\n## Running Tests\n\n* `ember test`\n* `ember test --server`\n\n## Release Process\n\nEvery commit to master results in a build and push to the demo\napplication at http://emberx-select.netlify.com\n\nNpm releases use semver and happen at the project owner's discretion.\n\n## Code of Conduct\n\nPlease note that this project is released with a Contributor Code of\nConduct. By participating in this project you agree to abide by its\nterms, which can be found in the `CODE_OF_CONDUCT.md` file in this\nrepository.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadopted-ember-addons%2Femberx-select","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadopted-ember-addons%2Femberx-select","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadopted-ember-addons%2Femberx-select/lists"}