Giter Club home page Giter Club logo

ampersand-select-view's Introduction

ampersand-select-view

Lead Maintainer: Christopher Dieringer (@cdaringe)

overview

A view module for intelligently rendering and validating selectbox input. Works well with ampersand-form-view.

install

npm install ampersand-select-view

Part of the Ampersand.js toolkit for building clientside applications.

API Reference

clear() - [Function] - returns this

Alias to calling setValue(null, true). Sets the selected option to either the unselectedText option or a user defined option whose value is null. Be mindful that if no unselectedText or null option exists, the view will error.

reset() - [Function] - returns this

Sets the selected option and view value to the original option value provided during construction.

setValue([value, skipValidationMessage]) - [Function] - returns this

Sets the selected option to that which matches the provided value. Updates the view's .value accordingly. SelectView will error if no matching option exists. null, undefined, and '' values will preferentially select unselectedText if defined.

constructor - [Function] new SelectView([options])

options

general options
  • name: the <select>'s name attribute's value. Used when reporting to parent form
  • parent: parent form reference
  • options: array/collection of options to render into the select box
  • [groupOptions]: use instead of options to generate <optgroup> elements within your <select>. If this is set, any values passed in options will be ignored and replaced with values coming from groupOptions.
  • [el]: element if you want to render the view into
  • [template]: a custom template to use (see 'template' section, below, for more)
  • [required]: [default: false] field required
  • [eagerValidate]: [default: false] validate and show messages immediately. Note: field will be validated immediately to provide a true .valid value, but messages by default are hidden.
  • [unselectedText]: text to display if unselected
  • [value]: initial value for the <select>. value must be a member of the options set
  • [tabindex]: [default: 0] Specify the tab index number for your field (integer).
label & validation options
  • [label]: [default: name value] text to annotate your select control
  • [invalidClass]: [default: 'select-invalid'] class to apply to root element if invalid
  • [validClass]: [default: 'select-valid'] class to apply to root element if valid
  • [requiredMessage]: [default: 'Selection required'] message to display if invalid and required
collection option set

If using a collection to produce <select> <option>s, the following may also be specified:

  • [disabledAttribute]: boolean model attribute to flag disabling of the option node
  • [idAttribute]: model attribute to use as the id for the option node. This will be returned by SelectView.prototype.value
  • [textAttribute]: model attribute to use as the text of the option node in the select box
  • [yieldModel]: [default: true] if options is a collection, yields the full model rather than just its idAttribute to .value

When the collection changes, the view will try and maintain its currently .value. If the corresponding model is removed, the <select> control will default to the 0th index <option> and update its value accordingly.

custom template

You may override the default template by providing your own template string to the constructor options hash. Technically, all you must provided is a <select> element. However, your template may include the following under a single root element:

  1. An element with a data-hook="label" to annotate your select control
  2. An <select> element to hold your options
  3. An element with a data-hook="message-container" to contain validation messages
  4. An element with a data-hook="message-text" nested beneath the data-hook="message-container" element to show validation messages

Here's the default template for reference:

<label class="select">
    <span data-hook="label"></span>
    <select></select>
    <span data-hook="message-container" class="message message-below message-error">
        <p data-hook="message-text"></p>
    </span>
</label>

You may SelectView.extend({ template: ...}) to create a View definition with a more permanent default template of your liking as well.

example

var FormView = require('ampersand-form-view');
var SelectView = require('ampersand-select-view');

module.exports = FormView.extend({
    fields: function () {
        return [
            new SelectView({
                label: 'Pick a color!',
                // actual field name
                name: 'color',
                parent: this,
                // you can pass simple string options
                options: ['blue', 'orange', 'red'],
                // if included this will add option for an unselected state
                unselectedText: 'please choose one',
                // you can specify that they have to pick one
                required: true,
                // specify tab index for usability
                tabindex: 1,
            }),
            new SelectView({
                name: 'option',
                parent: this,
                // you can also pass array, first is the value, second is used for the label
                // and an optional third value can used to disable the option
                options: [ ['a', 'Option A'], ['b', 'Option B'], ['c', 'Option C', true] ],
                tabindex: 2,
            }),
            new SelectView({
                name: 'option',
                parent: this,
                // define groupOptions to generate <optgroup> elements. Pass it an array of
                // Objects, each object will become an <optgroup> with groupName being the
                // <optgroup>'s name and options being an array (either of strings or array, see
                // previous two examples) that will become the <option>s under that <optgroup>
                groupOptions: [
                  {
                    groupName: "Options 1",
                    options: [ ['1', 'Option 1'], ['2', 'Option 2'], ['3', 'Option 3', true] ]
                  },
                  {
                    groupName: "Options 2",
                    options: [ ['a', 'Option A'], ['b', 'Option B'], ['c', 'Option C', true] ]
                  }
                ],
                tabindex: 3,
            }),
            new SelectView({
                name: 'model',
                parent: this,
                // you can pass in a collection here too
                options: collection,
                // and pick an item from the collection as the selected one
                value: collection1.at(2),
                // here you specify which attribute on the objects in the collection
                // to use for the value returned.
                idAttribute: 'id',
                // you can also specify which model attribute to use as the title
                textAttribute: 'title',
                // you can also specify a boolean model attribute to render items as disabled
                disabledAttribute: 'disabled',
                // here you can specify if it should return the selected model from the
                // collection, or just the id attribute.  defaults `true`
                yieldModel: false,
                tabindex: 4,
            })
        ];
    }
});

gotchas

  • Numeric option values are generally stringified by the browser. Be mindful doing comparisons. You'll generally desire to inspect selectView.value (the value of your selected options' input) over selectView.select.value (the value returned from the browser).

    • Additionally, do not use option sets containing values that == one another. E.g., do not use options whose values are "2" (string) and 2 (number). Browsers cannot distinguish between them in the select control context, thus nor can ampersand-select-view.
  • null, undefined, or '' option values are not considered valid when the field is required. This does not apply when options are from a collection and yieldModel is enabled.

    • The unselectedText option will always be preferred in updating the control to an empty-ish value.

browser support

testling badge

changelog

  • 7.0.0

    • Upgrade to &-view 9.x
  • 6.2.1

    • Added the tabindex option to allow custom tab-ordering of fields for usability
  • 6.2.0

    • Support extending template
  • 6.1.0

    • Generate <optgroup> elements by passing the new options.groupOptions parameter
  • 6.0.0

    • Match field label rendering behavior to ampersand-input-view. removes label fallback to name attr
    • Improve x-browser testing CI
  • 5.0.0

    • Change events now always get triggered on the select element instead of blindly calling on the root element.
  • 4.0.0

    • Extend ampersand-view and support autoRender, where previously this view would autoRender unconditionally
  • 3.0.0

    • Improve general option edge cases, and add supporting test cases. Primarily targets falsy option value handling.
    • Validate immediately to assist when parent FormView tests onload for field validity. Update skipValidation to skipValidationMessage, permit immediate validation, but conditionally display messages.
    • Throw an Error when trying to setValue(value) and an option matching the requested value does not exist. The exception to this is when the provided value is null, undefined, or '', and a null option value exists. Because the DOM can only recognize a single empty value for any <option>, which is the empty string '', only a single empty-ish option can only be supported by the view.
    • Support 0 value options, both in Model id's and array values.
    • Add eagerValidate.
    • Denote a plan for 4.x release
    • Bulk update README, and some cody tidying

credits

Originally designed & written by @philip_roberts.

license

MIT

ampersand-select-view's People

Contributors

achingbrain avatar bear avatar bryanspears avatar cdaringe avatar dhritzkiv avatar e2jk avatar hailwood avatar henrikjoreteg avatar imorrison avatar kamilogorek avatar kimpeeters avatar latentflip avatar lukekarrys avatar nickryall avatar timwis avatar wraithgar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ampersand-select-view's Issues

Allow unselectedtext option with collections

I would like to use a select control backed by an ampersand collection, but have no option selected by default. Currently only other option methods support this.

Pending PR from me to enable this.

onChange

Hi,

ref #11

there is no onChange event apart from that on the element.

There should be a change-event triggered on the view.

As of now, when extending, the onChange-method is un-overrridable (propbably due to the binding to this at construction?), and this.el is not avavilable in initialize, so hooking into the change-event is really cumbersome.

-Maybe I don't see the full picture, but this does not behave as I'd expect from working with the other AmpersandJs modules.

Cheers.

Ability to use the <optgroup> element

It would be great to be able to pass to options something along the lines of:

[ {"Options 1": ["Option 1.1", "Option 1.2"] }, {"Options 2": ["Option 2.1", "Option 2.2"] } ]

and get something like this as the generated <select>:

<select>
  <optgroup label="Options 1">
     <option>Option 1.1</option>
     <option>Option 1.2</option>
  </optgroup>
  <optgroup label="Options 2">
     <option>Option 2.1</option>
     <option>Option 2.2</option>
  </optgroup>
</select>

Example: http://jsfiddle.net/JaZAm/1/

How to implement multiselect in ampersand-select-view

I am providing the multiple attribute in my template. So in browser, I am able to select multiple values. However, When I submit the form, the value which is given to the server is only the first one out of all those. Is there a separate module for multiselect ?

Trigger `change` event when value is selected

Presently, it seems that the only way to get a notification when the value of the select view changes is to provide an update method on the parent view. This doesn't work well for a view that has multiple select fields or that wants to follow an orchestrator pattern where the child view knows nothing about and does not attempt to call methods on its parent.

We should provide a change event trigger whenever setValue is called.

Blocks Form Submission

If you don't pick a select value and leave it on the default value, the submitCallback never gets called

Number value type

When using a select view we should have the ability to specify the type of the value to be either string or number. Specifically we should be able to say that it is a number. Proposed solution interface can be found at https://gist.github.com/tdwire/8c6b0651bb564530bb36. A little more about this: by its very nature a value of an <option/> tag is a string - so this is the reason that we should be able to specify number so that it can be parsed as such. Otherwise we could just pass in numbers. This can be more readily seen by the following: http://jsfiddle.net/bpL5cjyc/

Validation error message not showing on parent form submit

I am using an ampersand-select-view within an ampersand-form-view like this:

new FormView({
  ...
  fields: [
    ...
    new SelectView({
      el: self.queryByHook('category'),
      name: 'category',
      options: categoryOptions,
      label: 'Main Category *',
      required: true,
      requiredMessage: "Please select a category."
    })
  ]
});

The select-view renders correctly. It is also considered in the form view's validation - selecting no option does prevent the submit. However, the error message is not displayed upon submit. The error is shown, though, if I select an entry and then deselect it again.

I use other ampersand-input-views in the same FormView, which work fine.

Doesn't render in el

Hey,
whenever I specify an el the SelectView doesn't get rendered at all. I use it in conjunction with ampersand-form-view.
I've tried the latest version on npm and master, all kinds of different combinations of autoAppend and autoRender to no avail.

new SelectView({
  el: this.el.querySelector('[data-hook=package-name]')
})

Without a custom el it works fine. My input-views in the form work fine as well (with custom el).
I'm not sure if it's a bug or if I'm doing something wrong, but I have no idea what to try next.

Thank you!

inconsistent API within ampersand-*-view ecosystem

To get the value from, say, ampersand-input-view you can get .value and expect the actual string value that would be sent when the form is submitted. This is not the case with select-view. This makes inspecting the entire form challenging, as it requires too much knowledge about the fields.

As part of trying to get ampersand-form-view a little easier to use, we should standardize the way these fields views work. Namely, getValue and setValue should exist on them all and should accept and return actual string values that translate 1:1 to what will end up in the serialized form.

Right now, select-view is the closest to having this ready to go, as it currently has getOptionValue (which works exactly like getValue should) and a setValue (which works exactly as described above).

This preserves whatever under-the-hood .value or .currentValue or what have you that each fields view is doing separately.

So my proposal for this repo is to add a getValue() that either mirrors or forwards functionality to getOptionValue

SauceLabs tests won't run on Pull Requests

@bear @cdaringe

I 'think' the issue with Sauce Labs may be that because the travis.yml is using the secure access key, only users with write access to the repo will be able to have the tests run on forked PRs.

In summary the solution is to either use the public keys in the travis.yml ( which would make the account prone to abuse e.g. using it to run tests on other projects ) or using something like this:

https://github.com/twbs/savage

Related discussion can be found here:

https://support.saucelabs.com/entries/25614798-How-can-we-set-up-an-open-source-account-that-runs-tests-on-people-s-pull-requests-

Please don't set the label to opts.name

Line 38:

this.label = opts.label || this.name; // init as string. render() mutates into DOM el with .textContent equal to this value

The way I'm using the select-view, when I deliberately set the label to nothing that means I don't want a label to appear. Additionally, the name I pass into the select-view options usually matches the model attribute and is rarely (if ever) a human friendly string.

If it has to be a string, then at least set it to one. I know an empty string returns false, but I didn't see any further check, and this way it would at least avoid unintended side effects of setting it to the name.

this.label = opts.label || ""; 

`GET` not working when `POST`/`PUT` was done with `yieldModel: false`

Situation: 2 models:

group which has:

    props: {
        id: 'any',
        abbreviation: ['string', true, ''],
        name: ['string', true, '']
    },

And object which has a group child (extract):

    var Group = require('./group');

    children: {
      relatedGroup: Group
    },

With the following form to edit the object (extract):

            var SelectView = require('ampersand-select-view');

            new SelectView({
                label: "Related Group",
                name: "relatedGroup",
                value: this.model && this.model.relatedGroup && this.model.relatedGroup.id,
                options: app.groups,
                required: false,
                idAttribute: 'id',
                textAttribute: 'fullName',
                yieldModel: false,
                parent: this
            })

Since yieldModel is set to False, only the Id of the child object is sent in the POST/PUT API call. That's fine.
When retrieving the object (either the single state or the collection), the fake API faithfully returns just the ID. I would think that's fine as well, since based on that ID we should be able to getOrFetch the state.

But with the following code in the view's initialize:

    initialize: function (spec) {
        var self = this;
        app.objects.getOrFetch(spec.id, {all: true}, function (err, model) {
            if (err) alert('couldnt find a model with id: ' + spec.id);
            self.model = model;
            console.log(self.model.relatedGroup);
            console.log(self.model.relatedGroup.id);

self.model.relatedGroup is an Object, with functionally empty: it has empty abbreviation and name string values, but id doesn't exist on the Object. Hence the call to self.model.relatedGroup.id returns undefined.
It looks like Ampersand.js tries to create the relatedGroup property on the object model, since abbreviation and name are present, but it somehow doens't understand the value it received from the server is just the ID.

Is there an equivalent to yieldModel but for the server > client direction? In other words, could the server just send the ID, and the client perform a getOrFetch on that collection with the ID returned?
Or does the server need to accept only the ID when POST/PUT, but send the full object when GET?

Form submit fails on attributes with the type 'number'

I'm attempting to use a select box to pick from an array of numbers with labels (i.e. [[1,'Once'], [2,'Twice'], ...]. However, since the attribute is set to 'number' in the model, this causes the form to fail. (Edit: more info below)

Reports invalid despite having a value

Passing a collection of options and choosing a default value still makes the field return false for validity.

Options:
screen shot 2014-07-10 at 7 06 12 pm

In situ:
screen shot 2014-07-10 at 7 02 57 pm

Asking for a value from the DOM explicitly:
screen shot 2014-07-10 at 7 04 21 pm

Any idea what may be causing this? Let me know if you need more information.

Error when mixing string and array in options definition

It's possible to pass simple string options (like options: ['blue', 'orange', 'red']) or pass array (value, label and optional to disable the option, like options: [ ['a', 'Option A'], ['b', 'Option B'], ['c', 'Option C', true] ]).

But if you mix string and array like this options: [ 'Option A', ['b', 'Option B'], ['c', 'Option C', true] ], an error is thrown when selecting one of the options that are not of the same type as the first option in the array:
Error: value not in set of provided options

This is due to the fact that in getOptionByValue, check is done based on the type of the first given option:

// find value value in options array
// find option, formatted [['val', 'text'], ...]
if (this.options.length && Array.isArray(this.options[0])) {
  for (var i = this.options.length - 1; i >= 0; i--) {
    if (this.options[i][0] == value) return this.options[i];
  }
}
// find option, formatted ['valAndText', ...] format
if (this.options.length && this.options.indexOf(value) !== -1) return value;
throw new Error('value not in set of provided options');

I think it would make sense to be able to mix both string and array definition, for example if the strings are OK as values but you want to disable just one item.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.