ionic-angular
Version:
A powerful framework for building mobile and progressive web apps with JavaScript and Angular
503 lines • 22.4 kB
JavaScript
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "@angular/core", "@angular/forms", "../action-sheet/action-sheet", "../alert/alert", "../popover/popover", "../app/app", "../../config/config", "../../navigation/deep-linker", "../../util/form", "../../util/base-input", "../../util/util", "../item/item", "../option/option", "./select-popover-component"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@angular/core");
var forms_1 = require("@angular/forms");
var action_sheet_1 = require("../action-sheet/action-sheet");
var alert_1 = require("../alert/alert");
var popover_1 = require("../popover/popover");
var app_1 = require("../app/app");
var config_1 = require("../../config/config");
var deep_linker_1 = require("../../navigation/deep-linker");
var form_1 = require("../../util/form");
var base_input_1 = require("../../util/base-input");
var util_1 = require("../../util/util");
var item_1 = require("../item/item");
var option_1 = require("../option/option");
var select_popover_component_1 = require("./select-popover-component");
/**
* @name Select
* @description
* The `ion-select` component is similar to an HTML `<select>` element, however,
* Ionic's select component makes it easier for users to sort through and select
* the preferred option or options. When users tap the select component, a
* dialog will appear with all of the options in a large, easy to select list
* for users.
*
* The select component takes child `ion-option` components. If `ion-option` is not
* given a `value` attribute then it will use its text as the value.
*
* If `ngModel` is bound to `ion-select`, the selected value will be based on the
* bound value of the model. Otherwise, the `selected` attribute can be used on
* `ion-option` components.
*
* ### Interfaces
*
* By default, the `ion-select` uses the {@link ../../alert/AlertController AlertController API}
* to open up the overlay of options in an alert. The interface can be changed to use the
* {@link ../../action-sheet/ActionSheetController ActionSheetController API} or
* {@link ../../popover/PopoverController PopoverController API} by passing `action-sheet` or `popover`,
* respectively, to the `interface` property. Read on to the other sections for the limitations
* of the different interfaces.
*
* ### Single Value: Radio Buttons
*
* The standard `ion-select` component allows the user to select only one
* option. When selecting only one option the alert interface presents users with
* a radio button styled list of options. The action sheet interface can only be
* used with a single value select. If the number of options exceed 6, it will
* use the `alert` interface even if `action-sheet` is passed. The `ion-select`
* component's value receives the value of the selected option's value.
*
* ```html
* <ion-item>
* <ion-label>Gender</ion-label>
* <ion-select [(ngModel)]="gender">
* <ion-option value="f">Female</ion-option>
* <ion-option value="m">Male</ion-option>
* </ion-select>
* </ion-item>
* ```
*
* ### Multiple Value: Checkboxes
*
* By adding the `multiple="true"` attribute to `ion-select`, users are able
* to select multiple options. When multiple options can be selected, the alert
* overlay presents users with a checkbox styled list of options. The
* `ion-select multiple="true"` component's value receives an array of all the
* selected option values. In the example below, because each option is not given
* a `value`, then it'll use its text as the value instead.
*
* Note: the `action-sheet` and `popover` interfaces will not work with a multi-value select.
*
* ```html
* <ion-item>
* <ion-label>Toppings</ion-label>
* <ion-select [(ngModel)]="toppings" multiple="true">
* <ion-option>Bacon</ion-option>
* <ion-option>Black Olives</ion-option>
* <ion-option>Extra Cheese</ion-option>
* <ion-option>Mushrooms</ion-option>
* <ion-option>Pepperoni</ion-option>
* <ion-option>Sausage</ion-option>
* </ion-select>
* </ion-item>
* ```
*
* ### Select Buttons
* By default, the two buttons read `Cancel` and `OK`. Each button's text
* can be customized using the `cancelText` and `okText` attributes:
*
* ```html
* <ion-select okText="Okay" cancelText="Dismiss">
* ...
* </ion-select>
* ```
*
* The `action-sheet` and `popover` interfaces do not have an `OK` button, clicking
* on any of the options will automatically close the overlay and select
* that value.
*
* ### Select Options
*
* Since `ion-select` uses the `Alert`, `Action Sheet` and `Popover` interfaces, options can be
* passed to these components through the `selectOptions` property. This can be used
* to pass a custom title, subtitle, css class, and more. See the
* {@link ../../alert/AlertController/#create AlertController API docs},
* {@link ../../action-sheet/ActionSheetController/#create ActionSheetController API docs}, and
* {@link ../../popover/PopoverController/#create PopoverController API docs}
* for the properties that each interface accepts.
*
* For example, to change the `mode` of the overlay, pass it into `selectOptions`.
*
* ```html
* <ion-select [selectOptions]="selectOptions">
* ...
* </ion-select>
* ```
*
* ```ts
* this.selectOptions = {
* title: 'Pizza Toppings',
* subTitle: 'Select your toppings',
* mode: 'md'
* };
* ```
*
* ### Object Value References
*
* When using objects for select values, it is possible for the identities of these objects to
* change if they are coming from a server or database, while the selected value's identity
* remains the same. For example, this can occur when an existing record with the desired object value
* is loaded into the select, but the newly retrieved select options now have different identities. This will
* result in the select appearing to have no value at all, even though the original selection in still intact.
*
* Using the `compareWith` `Input` is the solution to this problem
*
* ```html
* <ion-item>
* <ion-label>Employee</ion-label>
* <ion-select [(ngModel)]="employee" [compareWith]="compareFn">
* <ion-option *ngFor="let employee of employees" [value]="employee">{{employee.name}}</ion-option>
* </ion-select>
* </ion-item>
* ```
*
* ```ts
* compareFn(e1: Employee, e2: Employee): boolean {
* return e1 && e2 ? e1.id === e2.id : e1 === e2;
* }
* ```
*
* @demo /docs/demos/src/select/
*/
var Select = (function (_super) {
__extends(Select, _super);
function Select(_app, form, config, elementRef, renderer, item, deepLinker) {
var _this = _super.call(this, config, elementRef, renderer, 'select', [], form, item, null) || this;
_this._app = _app;
_this.config = config;
_this.deepLinker = deepLinker;
_this._multi = false;
_this._texts = [];
_this._text = '';
_this._compareWith = util_1.isCheckedProperty;
/**
* @input {string} The text to display on the cancel button. Default: `Cancel`.
*/
_this.cancelText = 'Cancel';
/**
* @input {string} The text to display on the ok button. Default: `OK`.
*/
_this.okText = 'OK';
/**
* @input {any} Any additional options that the `alert` or `action-sheet` interface can take.
* See the [AlertController API docs](../../alert/AlertController/#create) and the
* [ActionSheetController API docs](../../action-sheet/ActionSheetController/#create) for the
* create options for each interface.
*/
_this.selectOptions = {};
/**
* @input {string} The interface the select should use: `action-sheet`, `popover` or `alert`. Default: `alert`.
*/
_this.interface = '';
/**
* @input {string} The text to display instead of the selected option's value.
*/
_this.selectedText = '';
/**
* @output {any} Emitted when the selection was cancelled.
*/
_this.ionCancel = new core_1.EventEmitter();
return _this;
}
Object.defineProperty(Select.prototype, "compareWith", {
/**
* @input {Function} The function that will be called to compare object values
*/
set: function (fn) {
if (typeof fn !== 'function') {
throw new Error("compareWith must be a function, but received " + JSON.stringify(fn));
}
this._compareWith = fn;
},
enumerable: true,
configurable: true
});
Select.prototype._click = function (ev) {
ev.preventDefault();
ev.stopPropagation();
this.open(ev);
};
Select.prototype._keyup = function () {
this.open();
};
/**
* @hidden
*/
Select.prototype.getValues = function () {
var values = Array.isArray(this._value) ? this._value : [this._value];
(void 0) /* assert */;
return values;
};
/**
* Open the select interface.
*/
Select.prototype.open = function (ev) {
var _this = this;
if (this.isFocus() || this._disabled) {
return;
}
(void 0) /* console.debug */;
// the user may have assigned some options specifically for the alert
var selectOptions = util_1.deepCopy(this.selectOptions);
// make sure their buttons array is removed from the options
// and we create a new array for the alert's two buttons
selectOptions.buttons = [{
text: this.cancelText,
role: 'cancel',
handler: function () {
_this.ionCancel.emit(_this);
}
}];
// if the selectOptions didn't provide a title then use the label's text
if (!selectOptions.title && this._item) {
selectOptions.title = this._item.getLabelText();
}
var options = this._options.toArray();
if ((this.interface === 'action-sheet' || this.interface === 'popover') && this._multi) {
console.warn('Interface cannot be "' + this.interface + '" with a multi-value select. Using the "alert" interface.');
this.interface = 'alert';
}
if (this.interface === 'popover' && !ev) {
console.warn('Interface cannot be "popover" without UIEvent.');
this.interface = 'alert';
}
var overlay;
if (this.interface === 'action-sheet') {
selectOptions.buttons = selectOptions.buttons.concat(options.map(function (input) {
return {
role: (input.selected ? 'selected' : ''),
text: input.text,
handler: function () {
_this.value = input.value;
input.ionSelect.emit(input.value);
}
};
}));
var selectCssClass = 'select-action-sheet';
// If the user passed a cssClass for the select, add it
selectCssClass += selectOptions.cssClass ? ' ' + selectOptions.cssClass : '';
selectOptions.cssClass = selectCssClass;
overlay = new action_sheet_1.ActionSheet(this._app, selectOptions, this.config);
}
else if (this.interface === 'popover') {
var popoverOptions = options.map(function (input) { return ({
text: input.text,
checked: input.selected,
disabled: input.disabled,
value: input.value,
handler: function () {
_this.value = input.value;
input.ionSelect.emit(input.value);
}
}); });
var popoverCssClass = 'select-popover';
// If the user passed a cssClass for the select, add it
popoverCssClass += selectOptions.cssClass ? ' ' + selectOptions.cssClass : '';
overlay = new popover_1.Popover(this._app, select_popover_component_1.SelectPopover, {
options: popoverOptions
}, {
cssClass: popoverCssClass
}, this.config, this.deepLinker);
// ev.target is readonly.
// place popover regarding to ion-select instead of .button-inner
Object.defineProperty(ev, 'target', { value: ev.currentTarget });
selectOptions.ev = ev;
}
else {
// default to use the alert interface
this.interface = 'alert';
// user cannot provide inputs from selectOptions
// alert inputs must be created by ionic from ion-options
selectOptions.inputs = this._options.map(function (input) {
return {
type: (_this._multi ? 'checkbox' : 'radio'),
label: input.text,
value: input.value,
checked: input.selected,
disabled: input.disabled,
handler: function (selectedOption) {
// Only emit the select event if it is being checked
// For multi selects this won't emit when unchecking
if (selectedOption.checked) {
input.ionSelect.emit(input.value);
}
}
};
});
var selectCssClass_1 = 'select-alert';
// create the alert instance from our built up selectOptions
overlay = new alert_1.Alert(this._app, selectOptions, this.config);
if (this._multi) {
// use checkboxes
selectCssClass_1 += ' multiple-select-alert';
}
else {
// use radio buttons
selectCssClass_1 += ' single-select-alert';
}
// If the user passed a cssClass for the select, add it
selectCssClass_1 += selectOptions.cssClass ? ' ' + selectOptions.cssClass : '';
overlay.setCssClass(selectCssClass_1);
overlay.addButton({
text: this.okText,
handler: function (selectedValues) { return _this.value = selectedValues; }
});
}
overlay.present(selectOptions);
this._fireFocus();
overlay.onDidDismiss(function () {
_this._fireBlur();
_this._overlay = undefined;
});
this._overlay = overlay;
};
/**
* Close the select interface.
*/
Select.prototype.close = function () {
if (!this._overlay || !this.isFocus()) {
return;
}
return this._overlay.dismiss();
};
Object.defineProperty(Select.prototype, "multiple", {
/**
* @input {boolean} If true, the element can accept multiple values.
*/
get: function () {
return this._multi;
},
set: function (val) {
this._multi = util_1.isTrueProperty(val);
},
enumerable: true,
configurable: true
});
Object.defineProperty(Select.prototype, "text", {
/**
* @hidden
*/
get: function () {
return (this._multi ? this._texts : this._texts.join());
},
enumerable: true,
configurable: true
});
Object.defineProperty(Select.prototype, "options", {
/**
* @private
*/
set: function (val) {
this._options = val;
var values = this.getValues();
if (values.length === 0) {
// there are no values set at this point
// so check to see who should be selected
// we use writeValue() because we don't want to update ngModel
this.writeValue(val.filter(function (o) { return o.selected; }).map(function (o) { return o.value; }));
}
else {
this._updateText();
}
},
enumerable: true,
configurable: true
});
Select.prototype._inputShouldChange = function (val) {
return !util_1.deepEqual(this._value, val);
};
/**
* TODO: REMOVE THIS
* @hidden
*/
Select.prototype._inputChangeEvent = function () {
return this.value;
};
/**
* @hidden
*/
Select.prototype._updateText = function () {
var _this = this;
this._texts.length = 0;
if (this._options) {
this._options.forEach(function (option) {
// check this option if the option's value is in the values array
option.selected = _this.getValues().some(function (selectValue) {
return _this._compareWith(selectValue, option.value);
});
if (option.selected) {
_this._texts.push(option.text);
}
});
}
this._text = this._texts.join(', ');
};
/**
* @hidden
*/
Select.prototype._inputUpdated = function () {
this._updateText();
_super.prototype._inputUpdated.call(this);
};
Select.decorators = [
{ type: core_1.Component, args: [{
selector: 'ion-select',
template: '<div *ngIf="!_text" class="select-placeholder select-text">{{placeholder}}</div>' +
'<div *ngIf="_text" class="select-text">{{selectedText || _text}}</div>' +
'<div class="select-icon">' +
'<div class="select-icon-inner"></div>' +
'</div>' +
'<button aria-haspopup="true" ' +
'type="button" ' +
'[id]="id" ' +
'ion-button="item-cover" ' +
'[attr.aria-labelledby]="_labelId" ' +
'[attr.aria-disabled]="_disabled" ' +
'class="item-cover">' +
'</button>',
host: {
'[class.select-disabled]': '_disabled'
},
providers: [{ provide: forms_1.NG_VALUE_ACCESSOR, useExisting: Select, multi: true }],
encapsulation: core_1.ViewEncapsulation.None,
},] },
];
/** @nocollapse */
Select.ctorParameters = function () { return [
{ type: app_1.App, },
{ type: form_1.Form, },
{ type: config_1.Config, },
{ type: core_1.ElementRef, },
{ type: core_1.Renderer, },
{ type: item_1.Item, decorators: [{ type: core_1.Optional },] },
{ type: deep_linker_1.DeepLinker, },
]; };
Select.propDecorators = {
'cancelText': [{ type: core_1.Input },],
'okText': [{ type: core_1.Input },],
'placeholder': [{ type: core_1.Input },],
'selectOptions': [{ type: core_1.Input },],
'interface': [{ type: core_1.Input },],
'selectedText': [{ type: core_1.Input },],
'compareWith': [{ type: core_1.Input },],
'ionCancel': [{ type: core_1.Output },],
'_click': [{ type: core_1.HostListener, args: ['click', ['$event'],] },],
'_keyup': [{ type: core_1.HostListener, args: ['keyup.space',] },],
'multiple': [{ type: core_1.Input },],
'options': [{ type: core_1.ContentChildren, args: [option_1.Option,] },],
};
return Select;
}(base_input_1.BaseInput));
exports.Select = Select;
});
//# sourceMappingURL=select.js.map