Ember-bootstrap-controls is a small library for quickly creating EmberJS forms that utilize the Bootstrap form and input stylings.
This README outlines the details of using and collaborating on this Ember addon.
keyPress
,keyDown
,keyUp
events were all renamed tokey-press
,key-down
, andkey-up
respectively. This was to prevent it from conflicting with Ember.ComponentkeyPress
,keyDown
, andkeyUp
events. An assert is thrown to help the developer identify and fix these issues, but it is further recommeneded to do a project find, and replace where used.
This version is likely not backwards compatible with ember-cli < 2.11.0 and ember-cli-htmlbars < 1.1.1
- Find the
ember-bootstrap-controls
in yourpackage.json
which can be found in the root ember directory and update the version to the latest release. - Run
npm install
- Move into your root ember directory and run
ember g ember-bootstrap-controls
Add the following the list of dependencies in your package.json
which can be found in the root ember directory:
"ember-bootstrap-controls": "wildland/ember-bootstrap-controls#v0.16.0",
Now run npm install
.
Move into your root ember directory and run:
ember g ember-bootstrap-controls
See a live demo.
bootstrap-form
form helperbootstrap-debounce-input
input field helperbootstrap-input
input field helperbootstrap-mask-input
input field helperbootstrap-checkbox
checkbox input field helperbootstrap-currency-input
currency field helperbootstrap-textarea
text area helperbootstrap-datepicker
calendar style date select helperbootstrap-radio-group
radio button helperbootstrap-radio-group
checkbox button helperbootstrap-power-select
select tag helperbootstrap-multi-select
multi-select tag helperbootstrap-pagination-nav
pagination navigation helperbootstrap-data-table
table helper
We should write some documentation for this.
A field helper to simplify making an input field with a delay until the user stops typing or hits enter.
Use Example:
{{bootstrap-debounce-input value=search onChange=(action (mut value)) placeholder='Search'}}
Required Arguments:
value
- The attribute attached to the input.onChange
- The action to fire once the user is done typing
Optional Arguments:
label
- String displayed as the labels text.placeholder
- String displayed as the placeholder text.debounce
- Time in milliseconds to wait for the user to pause before updating the value attribute. Defaults to800
.errors
- Collection of DS.errors.
A field helper to simplify making an input field for a form.
Use Example:
{{bootstrap-input value=user.email label="Email”}}
Would render
<div class="form-group bootstrap-input-component">
<label for="bootstrap-component-0" class="control-label" >Email</label>
<input id="bootstrap-component-0" className="form-control ember-view ember-text-field">
<div>
Required Arguments:
value
- Ember model attribute attached to the input.label
- String displayed as the labels text.
Optional Arguments:
errors
- Collection of DS.errors.customLabelCss
- Custom css to be added to the label.srOnly
- Boolean srOnly class to the label for screen readers. This hides the label, but still allows screen readers/computers to read the label by keeping it in the DOM. If it is desirable to hide the label, set this totrue
.placeholder
- Used to set the placeholder value of the input.readonly
- Used to make the input read-only.type
- Set the type of input, usually text.tabindex
- Sets the tabindex for the input.required
- Make the input a required entry.inputId
- Sets theid
value of the DOM element.
A field helper to add custom formatted text-masks to inputs.
Use Examples
Template
{{bootstrap-mask-input
mask=myMask
value=model.phoneNumber
label="Phone #"
placeholderChar="0"
placeholder="(000) 000-0000"
errors=model.errors.phoneNumber
keepCharPositions=true
key-press=(action "saveModel" model)}}
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
myMask: ['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
actions: {
saveModel(model) {
model.save();
},
}
});
Rendered Output
<div class="ember-view form-group bootstrap-input-component" id="ember[1###]">
<label class="control-label" for="input-ember[1###]">Phone #</label>
<input id="input-ember[1###]" placeholder="(000) 000-0000" type="text" class="ember-view ember-text-field form-control">
</div>
<!--
NOTE:
- The Text Mask will be in place of the placeholder and persist through typing while forcing a formatted value.
- This input will autosave the model when user clicks out of input (See: focusOut below).
- Setting the `placeholderChar` to "0" forces the mask to render `(000) 000-0000`, leaving it blank or defaulted would render `(___) ___-____` (See: placeholderChar below).
-->
Required Arguments:
mask
- An array of Regex values containing each text-mask character.value
- Ember model attribute attached to the input.label
- String displayed as the labels text.
Optional Arguments:
- NOTE: Contains all optional values for
bootstrap-input
, plus the following: keepCharPositions
- Boolean value to set whether or not characters' spaces in the format will be saved when another character is deleted (Default is set totrue
).placeholder
- String used to set the placeholder value of the input.placeholderChar
- Sets the default character to be used in the mask, default setting is'_'
.focus-out
- An action passed to the control to call when the user navigates out of input.key-press
- An action passed to the control to call when a key is interacted with.key-up
- An action passed to the control to call when a key is released.key-down
- An action passed to the control to call when a key is pressed down.
A field helper to simplify making a checkbox field for a form.
Use Example:
{{bootstrap-checkbox
isChecked=false
label="I am a person"
value="Y"
onclick=(action "myClickAction")}}
Would render
<div class="checkbox">
<label>
<input
type="checkbox"
checked=false
disabled=false
required=false
autofocus=false
readonly=""
value="Y"
onclick="function <ember action>(this.checked,this.value);"
tabindex=0>
I am a person
</label>
<div>
Optional Arguments:
label
- String displayed as the labels text.name
- Set the input name attribute.isChecked
- Set the checked value of the checkbox when initializing.disabled
- Sets the checkbox to disabled.autofocus
- Sets the checkbox focus when initialized.tabindex
- Sets the tab index attribute of the input.required
- Sets the checkbox input to required.srOnly
- Boolean srOnly removes the label when set to true.onclick
- An action passed to the control to call when value changes. Sends checked state (true/false) and the value as params. Using onclick is more accurate than onchange for checkbox event handling.inputId
- Sets theid
value of the DOM element.
A field helper to simplify making an currency input field for a form.
Use Example:
{{bootstrap-currency-input
value=model.rate
label="Rate"
required=true
errors=model.errors.rate}}
$'s are also allowed to be prefixed to the number al la $55.45
, so this might need to be handled by business logic somewhere in the application. One pattern that could be used to achive this is (using ember-cli-accounting
):
import accounting from "accounting"
...
rateInCents: attr('number'),
rate: computed('rateInCents', {
get() {
return `${accounting.formatMoney(this.get('rateInCents') / 100.0)}`;
},
set(key, value) {
if (value) {
this.set('rateInCents', accounting.unformat(value) * 100);
return value;
} else {
this.set('attorneyBillRateInCents', null);
}
}
}),
This pattern stores the rate in cents, and converts it to/from a currency string through a computed property.
Required Arguments:
value
- Ember model attribute attached to the input.label
- String displayed as the labels text.
Optional Arguments:
mask
- Optionally change the default currency mask (see ember-text-mask for details)errors
- Collection of DS.errors.placeholder
- Placeholder value.srOnly
- Boolean srOnly class to the label for screen readers. This hides the label, but still allows screen readers/computers to read the label by keeping it in the DOM. If it is desirable to hide the label, set this totrue
.key-press
- Key press action.key-up
- Key up action.key-down
- Key down action.
A helper for simplifying textarea input fields.
Use Example:
{{bootstrap-textarea
value=value
label="label"
content=content
disabled=isNotEditing
placeholder="Enter text..."}}
Rendered output:
<textarea id="bootstrap-component-3" placeholder="Enter text here..." class="ember-view ember-text-area form-control"></textarea>
Required Arguments:
value
- Ember model attribute attached to the input.label
- String displayed as the labels text.
Optional Arguments:
errors
- Collection of DS.errors.customLabelCss
- Custom css to be added to the label.srOnly
- Boolean srOnly class to the label for screen readers. This hides the label, but still allows screen readers/computers to read the label by keeping it in the DOM. If it is desirable to hide the label, set this totrue
.
Use Example
{{bootstrap-datepicker
id=inputId
value=value
todayHighlight=todayHighlight
class="form-control"}}
Rendered Output is a <table>
structured like a calandar which allows the user to choose a specific date.
Required Arguments:
value
- Ember model attribute attached to the input.label
- String displayed as the labels text.
Optional Arguments:
errors
- Collection of DS.errors.customLabelCss
- Custom css to be added to the label.todayHighlight
- Highlights the current date on calandar UI.format
- Format for date. Defaults to 'mm/dd/yyyy'.changeDate
- The changeDate action is triggered when the selected date changes.srOnly
- Boolean srOnly class to the label for screen readers. This hides the label, but still allows screen readers/computers to read the label by keeping it in the DOM. If it is desirable to hide the label, set this totrue
.
Use Example:
Template
{{#bootstrap-radio-group
label="Favorite Animal"
selected=selectedItem
options=names
errors=errors
onChange=(action 'selectValue')
as |option|}}
<span>{{option}}</span>
{{/bootstrap-radio-group}}
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
names: ['Cat', 'Dog', 'Hamster'],
selectedItem: null,
actions: {
selectValue(selectedItem) {
this.set("selectedItem", selectedItem);
}
}
});
Required Arguments:
label
- String displayed as the fieldset legend.selected
- The selected optionoptions
- Colletion of options to display in the componentonChange
- The function to be invoked when the user checks an option.
Optional Arguments:
errors
- Collection of DS.errors.
Use Example:
Template
{{#bootstrap-checkbox-group
label="What pets do you own?"
selected=selectedItems
options=names
errors=errors
onChange=(action 'selectValue')
as |option|}}
<span>{{option}}</span>
{{/bootstrap-checkbox-group}}
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
names: ['Cat', 'Dog', 'Hamster'],
selectedItems: [],
actions: {
selectValue(newSelections, oldSelections) {
this.set('selectedItems', newSelections);
}
}
});
Required Arguments:
label
- String displayed as the fieldset legend.selected
- The selected optionoptions
- Colletion of options to display in the componentonChange
- The function to be invoked when the user checks an option.
Optional Arguments:
errors
- Collection of DS.errors.
Use Example:
Template
{{#bootstrap-power-select
label="Name"
selected=selectedItem
options=names
placeholder="Pick a name"
onChange=(action "foo")
allowClear=true
srOnly=false
loadingMessage="Loading..."
as |option|}}
<span>{{option}}</span>
{{/bootstrap-power-select}}
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
names: ['Stefan', 'Miguel', 'Tomster', 'Pluto'],
selectedItem: null,
actions: {
foo(selectedItem) {
this.set("selectedItem", selectedItem);
}
}
});
Required Arguments:
label
- String displayed as the labels text.selected
- The selected optionoptions
- Colletion of options to display in the componentonChange
- The function to be invoked when the user selects or unselects an option.
Optional Arguments:
errors
- Collection of DS.errors.allowClear
- When truthy, single selects allow to nullify the selectionloadingMessage
- Message shown in the list of options while the options are still not resolved, tipically after a search but also when options is a promise.placeholder
- Text to display in the trigger of the component while no option is selectedsearchEnabled
- When falsey, hides the search in single selectssearchField
- When the options are objects and no custom matches function is provided, this option tells the component what property of the options should the default matches use to filtersrOnly
- When set to true, this will apply the "sr-only" class to the the label element causing it to be hiddendisabled
- When truthy the component cannot be interactedrenderInPlace
- When truthy, the list of options will be rendered in place instead of being attaches to the root of the body and positioned with javascript. Enabling this option also adds a wrapper div around the trigger and the content with class .ember-power-select. Useful when the power-select is inside of a modal.matcher
- Sometimes the default matcher is not enough for you, for example if you need to match against several fields or you need to perform fuzzy matching. If that is the case just pass your own matcher function. It will receive the option and the search term and you can do whatever you feel like inside as long as it returns -1 if it doesn't match and a positive number if it does.
Example matcher
that searches from the start of each item string:
firstLetterMatcher(listItem, term) {
if(listItem.toLowerCase().startsWith(term.toLowerCase())) {
return 1;
} else {
return -1;
}
},
Lazy power select is only built to handle large datasets and must use the search action because the page of the data that will be paginated through must be known, which cannot be known before a search key has been established. Thus, do not expect a search to work unless the search
attribute is used instead of options
. Lazy power select, in order to keep track of the total pages available, expects the promise to return { options, pageCount }
where pageCount
is the total number of pages, and options
is an enumerable of the fetched options to show.
Required Arguments:
label
- String displayed as the labels text.selected
- The selected optionsearchByPage
- When called, this passes thesearchTerm
and thepage
to load. Must return{ options, pageCount }
whereoptions
are the resolved page options, andpageCount
is the total count of pages that can be loaded. When the user scrolls to the bottom of the list,bootstrap-power-select-lazy
will callsearchByPage
to load the next page of results.onChange
- The function to be invoked when the user selects or unselects an option.
Optional Arguments:
debounceMS
- Used to avoid firing a request on every letter input
Example Use
Use Example:
Template
{{#bootstrap-multi-select
label="Name"
selected=selectedItems
options=names
placeholder="Pick a name"
onChange=(action (mut selectedOptions))
allowClear=true
loadingMessage="Loading..."
as |option|}}
<span>{{option}}</span>
{{/bootstrap-multi-select}}
Same options as bootstrap-power-select
.
Note that this requires api_me >= v0.7.0
Use Example:
Template
{{bootstrap-pagination-nav
pageNumber=pageNumber
pageSize=pageSize
totalPages=totalPages
moveToPage=(action (mut pageNumber))}}
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ['pageNumber', 'pageSize']
/*
Note that you would combine these with existing queryParams.
So if you had:
queryParams: ['search']
It would become:
queryParams: ['search', 'pageNumber', 'pageSize']
*/
pageNumber: 1,
pageSize: 15,
});
Router
import Ember from 'ember';
import PaginatedRouteMixin from 'ember-bootstrap-controls/mixins/routes/pagination'; // Import the mixin
export default Ember.Route.extend(PaginatedRouteMixin, { //Extend from the mixin
model: function(params) {
return this.get('store').query('someModel', {
page: this.paginationParams(params), // Pass this information along with the query.
});
},
afterModel: function(model) {
this._super(...arguments); // Call the afterModel from the mixin
},
});
Use Example:
{{bootstrap-data-table
dataArray=users
title="User Index"
bordered=true
columns=(array
(hash
attr="fullName"
label="Name"
)
(hash
attr="username"
label="Username"
hasLink=true
idOnRoute=true
url="users.show"
)
(hash
attr="phone"
label="Contact #"
)
(hash
attr="email"
label="Email"
hasLink=true
idOnRoute=false
url="contacts.index"
)
(hash
attr="employment_organization.label"
label="Work"
)
<!-- NOTE: When calling an association of the dataArray model, you must use snke case instead oc camel case. -->
)
}}
Rendered Output:
<h3 class="text-center">{{title}}</h3>
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Username</th>
<th>Contact #</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{user.fullName}}</td>
<td><a href="users/show/:id">{{user.username}}</a></td>
<td>{{user.phone}}</td>
<td><a href="contacts/index">{{user.email}}</a></td>
</tr>
<tr>
<td>{{user.fullName}}</td>
<td><a href="users/show/:id">{{user.username}}</a></td>
<td>{{user.phone}}</td>
<td><a href="contacts/index">{{user.email}}</a></td>
</tr>
<tr>
<td>{{user.fullName}}</td>
<td><a href="users/show/:id">{{user.username}}</a></td>
<td>{{user.phone}}</td>
<td><a href="contacts/index">{{user.email}}</a></td>
</tr>
<!-- ...etc... -->
</tbody>
</table>
Required Arguments:
dataArray
- Array of Ember models to display on the table as rows (Note: At this time only Ember model objects work in thedataArray
).columns
- Array of Hashes containing column data.attr
- Name of Model's attribute to be displayed in column.label
- String displayed as the column's header text.
Optional Arguments:
title
- String displayed as the table's title.titleSmall
- Boolean, whentrue
reduces the size of the table's title.striped
- Boolean, whentrue
adds alternating shaded rows to table.responsive
- Boolean, whentrue
enabled horizontal scrolling on table and hides y-axis overflow.bordered
- Boolean, whentrue
adds border to table and columnscanHover
- Boolean, whentrue
enables row shading on mouse hover.condensed
- Boolean, whentrue
removes cell padding for a smaller table.numberedRows
- Boolean, whentrue
displays row number in first column.- Optional Column Arguments
hasLink
- Boolean, whentrue
wraps the column's content in a link tag.idOnRoute
- Boolean, whentrue
adds the id of the row's model to the url (used mainly for show and edit routes).url
- String value of Ember route (i.e."posts.show"
)
All optional argument values are false
as default
Styling:
Adding custom styling to the table is simple, just add a few handy class selectors to your css, scss, or less files and style normally. Here are the class selectors:
ember-bs-data-table-control
- The outside wrap<div>
for the table component.ember-bs-data-table
- Selector for the<table>
element itselfember-bs-table-header-row
- The<tr>
element inside of<thead>
.ember-bs-table-header-cell
- The<td>
element inside of<thead>
.ember-bs-table-row
- The<tr>
element inside of<tbody>
.ember-bs-table-cell
- The<td>
element inside of<tbody>
.
Note: Width is determined by size of the table's parent element.
For more information on Bootstrap style tables, visit the Bootstrap website.
If you are running ember-freestyle then you can add the following component to get a {{freestyle-section}}
that contains all of this library's components.
{{#freestyle-guide
...
{{ember-bootstrap-controls-freestyle}}
...
{{/freestyle-guide}}
ember server
- Visit your app at http://localhost:4200.
ember test
ember test --server
ember build
For more information on using ember-cli, visit http://www.ember-cli.com/.