diff --git a/docs/output-data-providers.md b/docs/output-data-providers.md
index 372e355..a574f3d 100644
--- a/docs/output-data-providers.md
+++ b/docs/output-data-providers.md
@@ -1,20 +1,19 @@
-Data Providers
-==============
+# Data Providers
In the [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html) sections, we have described how to
allow end users to choose a particular page of data to display and sort them by some columns. Because the task
-of paginating and sorting data is very common, Pwoli provides a set of *data provider* classes to encapsulate it.
+of paginating and sorting data is very common, Pwoli provides a set of _data provider_ classes to encapsulate it.
A data provider is a class extending [DataProvider](/pwoli/api-docs/classes/DataProvider.html). It mainly supports retrieving paginated
-and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can
-interactively paginate and sort data.
+and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can
+interactively paginate and sort data.
The following data provider classes are included in the Pwoli releases:
-* [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases
- and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances.
-* [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting
- specifications.
+- [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases
+ and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances.
+- [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting
+ specifications.
The usage of all these data providers share the following common pattern:
@@ -35,7 +34,7 @@ let count = provider.getCount();
let totalCount = provider.getTotalCount();
```
-You specify the pagination and sorting behaviors of a data provider by configuring its
+You specify the pagination and sorting behaviors of a data provider by configuring its
[pagination](/pwoli/api-docs/classes/DataProvider.html#pagination) and [sort](/pwoli/api-docs/classes/DataProvider.html#sort) properties
which correspond to the configurations for [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html), respectively.
You may also configure them to be `false` to disable pagination and/or sorting features.
@@ -52,18 +51,18 @@ let grid = new GridView({
In the [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html) sections, we have described how to
allow end users to choose a particular page of data to display and sort them by some columns. Because the task
-of paginating and sorting data is very common, Pwoli provides a set of *data provider* classes to encapsulate it.
+of paginating and sorting data is very common, Pwoli provides a set of _data provider_ classes to encapsulate it.
A data provider is a class implementing [DataProvider](/pwoli/api-docs/classes/DataProvider.html). It mainly supports retrieving paginated
-and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can
-interactively paginate and sort data.
+and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can
+interactively paginate and sort data.
The following data provider classes are included in the Pwoli releases:
-* [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases
- and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances.
-* [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting
- specifications.
+- [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases
+ and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances.
+- [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting
+ specifications.
The usage of all these data providers share the following common pattern:
@@ -84,7 +83,7 @@ let count = provider.getCount();
let totalCount = provider.getTotalCount();
```
-You specify the pagination and sorting behaviors of a data provider by configuring its
+You specify the pagination and sorting behaviors of a data provider by configuring its
[pagination](/pwoli/api-docs/classes/DataProvider.html#pagination) and [sort](/pwoli/api-docs/classes/DataProvider.html#sort) properties
which correspond to the configurations for [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html), respectively.
You may also configure them to be `false` to disable pagination and/or sorting features.
@@ -101,8 +100,7 @@ let grid = new GridView([
These data providers mainly vary in the way how the data source is specified. In the following subsections,
we will explain the detailed usage of each of these data providers.
-
-## Active Data Provider
+## Active Data Provider
To use [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html), you should configure its [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) property.
It can take a [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) object. If the former, the data returned will be arrays;
@@ -110,9 +108,9 @@ if the latter, the data returned can be either arrays or [Active Record](/pwoli/
For example,
```js
-import { ActiveDataProvider} from 'pwoli';
+import { ActiveDataProvider } from 'pwoli';
-let query = where({status: 1});
+let query = where({ status: 1 });
let provider = new ActiveDataProvider({
query: {},
@@ -122,8 +120,8 @@ let provider = new ActiveDataProvider({
sort: {
defaultOrder: {
created_at: SORT_DESC,
- title: SORT_ASC,
- }
+ title: SORT_ASC,
+ },
},
});
@@ -136,17 +134,16 @@ If `query` in the above example is created using the following code, then the da
```js
import Query;
-let query = ({where: {status: 1}});
+let query = ({where: {status: 1}});
```
> Note: If a query already specifies the `orderBy` clause, the new ordering instructions given by end users
- (through the `sort` configuration) will be appended to the existing `orderBy` clause. Any existing `limit`
- and `offset` clauses will be overwritten by the pagination request from end users (through the `pagination` configuration).
+> (through the `sort` configuration) will be appended to the existing `orderBy` clause. Any existing `limit`
+> and `offset` clauses will be overwritten by the pagination request from end users (through the `pagination` configuration).
By default, [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html) uses the `db` application component as the database connection. You may
use a different database connection by configuring the [db](/pwoli/api-docs/classes/ActiveDataProvider.html#db) property.
-
## Array Data Provider
[ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html) is best used when working with a big array. The provider allows you to return
@@ -178,22 +175,21 @@ let provider = new ArrayDataProvider({
// get the rows in the currently requested page
let rows = provider.getModels();
-```
-
-> Note: Compared to [Active Data Provider](#active-data-provider),array data provider is less efficient because it requires loading *all* data into the memory.
+```
+> Note: Compared to [Active Data Provider](#active-data-provider),array data provider is less efficient because it requires loading _all_ data into the memory.
## Working with Data Keys
When using the data items returned by a data provider, you often need to identify each data item with a unique key.
For example, if the data items represent customer information, you may want to use the customer ID as the key
-for each customer data. Data providers can return a list of such keys corresponding with the data items returned
+for each customer data. Data providers can return a list of such keys corresponding with the data items returned
by [DataProvider.getModels()](/pwoli/api-docs/classes/DataProvider.html#getModels). For example,
```js
import { ActiveDataProvider } from 'pwoli';
-let query = {where: {status: 1}};
+let query = { where: { status: 1 } };
let provider = new ActiveDataProvider({
query: {},
@@ -223,23 +219,22 @@ provider = new ActiveDataProvider({
query: {},
key: function (model) {
return md5(model.id);
- }
+ },
});
```
-
## Creating Custom Data Provider
To create your own custom data provider classes, you should implement [DataProvider](/pwoli/api-docs/classes/DataProvider.html).
An easier way is to extend from [DataProvider](/pwoli/api-docs/classes/DataProvider.html) which allows you to focus on the core data provider
logic. In particular, you mainly need to implement the following methods:
-
-- [prepareModels](/pwoli/api-docs/classes/DataProvider.html#prepareModels): prepares the data models that will be made
- available in the current page and returns them as an array.
-- [prepareKeys](/pwoli/api-docs/classes/DataProvider.html#prepareKeys): accepts an array of currently available data models
- and returns keys associated with them.
-- [PrepareTotalCount](/pwoli/api-docs/classes/DataProvider.html#prepareTotalCount): returns a value indicating the total number
- of data models in the data provider.
+
+- [prepareModels](/pwoli/api-docs/classes/DataProvider.html#prepareModels): prepares the data models that will be made
+ available in the current page and returns them as an array.
+- [prepareKeys](/pwoli/api-docs/classes/DataProvider.html#prepareKeys): accepts an array of currently available data models
+ and returns keys associated with them.
+- [PrepareTotalCount](/pwoli/api-docs/classes/DataProvider.html#prepareTotalCount): returns a value indicating the total number
+ of data models in the data provider.
Below is an example of a data provider that reads CSV data efficiently:
@@ -252,37 +247,37 @@ class CsvDataProvider extends DataProvider
* @var string name of the CSV file to read
*/
public filename;
-
+
/**
* @var string|callable name of the key column or a callable returning it
*/
public key;
-
+
/**
* @var SplFileObject
*/
protected fileObject; // SplFileObject is very convenient for seeking to particular line in a file
-
-
+
+
/**
* {@inheritdoc}
*/
- public function init()
+ public init()
{
parent.init();
-
+
// open file
this.fileObject = new SplFileObject(this.filename);
}
-
+
/**
* {@inheritdoc}
*/
- protected function prepareModels()
+ protected prepareModels()
{
models = [];
pagination = this.getPagination();
-
+
if (pagination === false) {
// in case there's no pagination, read all lines
while (!this.fileObject.eof()) {
@@ -294,24 +289,24 @@ class CsvDataProvider extends DataProvider
pagination.totalCount = this.getTotalCount();
this.fileObject.seek(pagination.getOffset());
limit = pagination.getLimit();
-
+
for (count = 0; count < limit; ++count) {
models[] = this.fileObject.fgetcsv();
this.fileObject.next();
}
}
-
+
return models;
}
-
+
/**
* {@inheritdoc}
*/
- protected function prepareKeys(models)
+ protected prepareKeys(models)
{
if (this.key !== null) {
keys = [];
-
+
foreach (models as model) {
if (is_string(this.key)) {
keys[] = model[this.key];
@@ -319,81 +314,31 @@ class CsvDataProvider extends DataProvider
keys[] = call_user_func(this.key, model);
}
}
-
+
return keys;
}
return array_keys(models);
}
-
+
/**
* {@inheritdoc}
*/
- protected function prepareTotalCount()
+ protected prepareTotalCount()
{
count = 0;
-
+
while (!this.fileObject.eof()) {
this.fileObject.next();
++count;
}
-
+
return count;
}
}
```
-## Filtering Data Providers using Data Filters
+## Filtering Data Providers
-While you can build conditions for active data provider manually as described in
+You can easily build conditions for filtering data with active data provider manually as described in
[Filtering Data](/pwoli/output-data-widgets.md#filtering-data) and [Separate Filter Form](/pwoli/output-data-widgets.md#separate-filter-form)
-sections of data widgets guide, Pwoli has data filters that are very useful if you need flexible filter conditions.
-Data filters could be used as follows:
-
-```js
-let filter = new ActiveDataFilter({
- searchModel: 'app\models\PostSearch'
-});
-
-let filterCondition = null;
-
-// You may load filters from any source. For example,
-// if you prefer JSON in request body,
-// use Pwoli.app.request.getBodyParams() below:
-if (filter.load(\Pwoli.app.request.get())) {
- filterCondition = filter.build();
- if (filterCondition === false) {
- // Serializer would get errors out of it
- return filter;
- }
-}
-
-let query = {};
-if (filterCondition !== null) {
- query.andWhere(filterCondition);
-}
-
-return new ActiveDataProvider({
- query: {},
-});
-```
-
-`PostSearch` model serves the purpose of defining which properties and values are allowed for filtering:
-
-```js
-import { Model } from 'pwoli';
-
-class PostSearch extends Model
-{
- public id;
- public title;
-
- public function rules()
- {
- return [
- ['id', 'integer'],
- ['title', 'string', {min: 2}, {max: 200}],
- ];
- }
-}
-```
\ No newline at end of file
diff --git a/src/helpers/DataHelper.ts b/src/helpers/DataHelper.ts
index b1b80d1..cfa9ffe 100644
--- a/src/helpers/DataHelper.ts
+++ b/src/helpers/DataHelper.ts
@@ -522,4 +522,50 @@ export default class DataHelper extends Component {
return defaultValue;
}
+
+ public static substrCompare(mainStr, str, offset, length, caseInsensitivity = false) {
+ // eslint-disable-line camelcase
+ // discuss at: https://locutus.io/php/substr_compare/
+ // original by: Brett Zamir (https://brett-zamir.me)
+ // original by: strcasecmp, strcmp
+ // example 1: substr_compare("abcde", "bc", 1, 2)
+ // returns 1: 0
+ if (!offset && offset !== 0) {
+ throw new Error('Missing offset for substr_compare()');
+ }
+ if (offset < 0) {
+ offset = mainStr.length + offset;
+ }
+ if (length && length > mainStr.length - offset) {
+ return false;
+ }
+ length = length || mainStr.length - offset;
+ mainStr = mainStr.substr(offset, length);
+ // Should only compare up to the desired length
+ str = str.substr(0, length);
+ if (caseInsensitivity) {
+ // Works as strcasecmp
+ mainStr = (mainStr + '').toLowerCase();
+ str = (str + '').toLowerCase();
+ if (mainStr === str) {
+ return 0;
+ }
+ return mainStr > str ? 1 : -1;
+ }
+ // Works as strcmp
+ return mainStr === str ? 0 : mainStr > str ? 1 : -1;
+ }
+
+ public static strcmp(str1, str2) {
+ // http://kevin.vanzonneveld.net
+ // + original by: Waldo Malqui Silva
+ // + input by: Steve Hilder
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ // + revised by: gorthaur
+ // * example 1: strcmp( 'waldo', 'owald' );
+ // * returns 1: 1
+ // * example 2: strcmp( 'owald', 'waldo' );
+ // * returns 2: -1
+ return str1 == str2 ? 0 : str1 > str2 ? 1 : -1;
+ }
}
diff --git a/src/helpers/Html.ts b/src/helpers/Html.ts
index 203317d..fefde94 100644
--- a/src/helpers/Html.ts
+++ b/src/helpers/Html.ts
@@ -2,11 +2,16 @@ import Model from '../base/Model';
import Pwoli from '../base/Application';
import Component from '../base/Component';
import DataHelper from './DataHelper';
-import DataProvider from '../data//DataProvider';
/**
- * BaseHtml provides concrete implementation for [[Html]].
+ * Html provides a set of static methods for generating commonly used HTML tags.
*
- * Do not use BaseHtml. Use [[Html]] instead.
+ * Nearly all of the methods in this class allow setting additional html attributes for the html
+ * tags they generate. You can specify, for example, `class`, `style` or `id` for an html element
+ * using the `$options` parameter. See the documentation of the [[tag]] method for more details.
+ *
+ * For more details and usage information on Html, see the [guide article on html helpers](guide:helper-html).
+ *
+ * @author Mahesh S Warrier
*/
export default class Html extends Component {
/**
@@ -1043,7 +1048,7 @@ export default class Html extends Component {
* @return the generated drop-down list tag
*/
public static activeDropDownList(model, attribute, items, options: any = {}) {
- if (options.multiple.length === 0) {
+ if (options.multiple !== true) {
return this.activeListInput('dropDownList', model, attribute, items, options);
}
@@ -1205,7 +1210,222 @@ export default class Html extends Component {
if (options.id === undefined) {
options.id = this.getInputId(model, attribute);
}
-
return this[type](name, selection, items, options);
}
+ /**
+ * Generates a drop-down list.
+ * @param name the input name
+ * @param selection the selected value(s). String for single or array for multiple selection(s).
+ * @param items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * `Array.map()`.
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
+ * to override the value and to set other tag attributes:
+ *
+ * ```js
+ * { text: 'Please select', options: { value: 'none', class: 'prompt', label: 'Select' } },
+ * ```
+ *
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ *
+ * ```js
+ * {
+ * value1: { disabled: true },
+ * value2: { label: 'value 2' },
+ * }
+ * ```
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with ` ` character.
+ * Defaults to false.
+ * - encode: bool, whether to encode option prompt and option value characters.
+ * Defaults to `true`. This option is available since 2.0.3.
+ * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode]]. If a value is null, the corresponding attribute will not be rendered.
+ * See [[renderTagAttributes]] for details on how attributes are being rendered.
+ *
+ * @return The generated drop-down list tag
+ */
+ public static dropDownList(name, selection = null, items = [], options: { [key: string]: any } = {}) {
+ if (options.multiple !== undefined) {
+ return this.listBox(name, selection, items, options);
+ }
+ options['name'] = name;
+ delete options['unselect'];
+ let selectOptions = this.renderSelectOptions(selection, items, options);
+ return this.tag('select', '\n' + selectOptions + '\n', options);
+ }
+ /**
+ * Generates a list box.
+ * @param name the input name
+ * @param selection the selected value(s). String for single or array for multiple selection(s).
+ * @param items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * `Array.map()`.
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
+ * to override the value and to set other tag attributes:
+ *
+ * ```js
+ * { text: 'Please select', options: { value: 'none', class: 'prompt', label: 'Select' } },
+ * ```
+ *
+ * - options: array, the attributes for the select option tags. The array keys must be valid option values,
+ * and the array values are the extra attributes for the corresponding option tags. For example,
+ * ```js
+ * {
+ * value1: { disabled: true },
+ * value2: { label: 'value 2' },
+ * }
+ * ```
+ * ```
+ *
+ * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
+ * except that the array keys represent the optgroup labels specified in $items.
+ * - unselect: string, the value that will be submitted when no option is selected.
+ * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
+ * mode, we can still obtain the posted unselect value.
+ * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with ` ` character.
+ * Defaults to false.
+ * - encode: bool, whether to encode option prompt and option value characters.
+ * Defaults to `true`.
+ * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.
+ *
+ * The rest of the options will be rendered as the attributes of the resulting tag. The values will
+ * be HTML-encoded using [[encode]]. If a value is null, the corresponding attribute will not be rendered.
+ * See [[renderTagAttributes]] for details on how attributes are being rendered.
+ *
+ * @return The generated list box tag
+ */
+ public static listBox(name: string, selection = null, items = [], options: { [key: string]: any } = {}) {
+ if (options.size === undefined) {
+ options['size'] = 4;
+ }
+ if (options.multiple !== undefined && name.length > 0 && DataHelper.substrCompare(name, '[]', -2, 2)) {
+ name += '[]';
+ }
+ options['name'] = name;
+ let hidden;
+ if (options['unselect'] !== undefined) {
+ // add a hidden field so that if the list box has no option being selected, it still submits a value
+ if (name.length > 0 && DataHelper.substrCompare(name, '[]', -2, 2) === 0) {
+ name = name.substr(0, -2);
+ }
+ let hiddenOptions = [];
+ // make sure disabled input is not sending any value
+ if (options['disabled'] !== undefined) {
+ hiddenOptions['disabled'] = options['disabled'];
+ }
+ hidden = this.hiddenInput(name, options['unselect'], hiddenOptions);
+ delete options['unselect'];
+ } else {
+ hidden = '';
+ }
+ let selectOptions = this.renderSelectOptions(selection, items, options);
+ return hidden + this.tag('select', '\n' + selectOptions + '\n', options);
+ }
+ /**
+ * Renders the option tags that can be used by [[dropDownList]] and [[listBox]].
+ * @param selection the selected value(s). String for single or array for multiple selection(s).
+ * @param items the option data items. The array keys are option values, and the array values
+ * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
+ * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
+ * If you have a list of data models, you may convert them into the format described above using
+ * `Array.map()`
+ *
+ * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
+ * the labels will also be HTML-encoded.
+ * @param tagOptions the $options parameter that is passed to the [[dropDownList]] or [[listBox]] call.
+ * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
+ * in [[dropDownList]] for the explanation of these elements.
+ *
+ * @return String the generated list options
+ */
+ public static renderSelectOptions(selection, items, tagOptions: { [key: string]: any } = {}) {
+ var lines,
+ encodeSpaces,
+ encode,
+ strict,
+ promptOptions,
+ promptText,
+ options,
+ groups,
+ key,
+ value,
+ groupAttrs,
+ attrs,
+ content,
+ text;
+ lines = [];
+ encodeSpaces = DataHelper.remove(tagOptions, 'encodeSpaces', false);
+ encode = DataHelper.remove(tagOptions, 'encode', true);
+ strict = DataHelper.remove(tagOptions, 'strict', false);
+ if (tagOptions.prompt !== undefined) {
+ promptOptions = { value: '' };
+ if (typeof tagOptions['prompt'] === 'string') {
+ promptText = tagOptions['prompt'];
+ } else {
+ promptText = tagOptions['prompt']['text'];
+ promptOptions = { ...promptOptions, ...tagOptions['prompt']['options'] };
+ }
+ promptText = encode ? this.encode(promptText) : promptText;
+ if (encodeSpaces) {
+ promptText = promptText.replace(' ', ' ');
+ }
+ lines.push(this.tag('option', promptText, promptOptions));
+ }
+
+ options = tagOptions.options !== undefined ? tagOptions['options'] : {};
+ groups = tagOptions.groups !== undefined ? tagOptions['groups'] : {};
+ delete tagOptions.prompt, tagOptions.options, tagOptions.groups;
+ options.encodeSpaces = DataHelper.getValue(options, 'encodeSpaces', encodeSpaces);
+ options.encode = DataHelper.getValue(options, 'encode', encode);
+
+ for (key in items) {
+ value = items[key];
+ if (typeof value === 'object') {
+ groupAttrs = groups[key] !== undefined ? groups[key] : {};
+ if (groupAttrs['label'] === undefined) groupAttrs['label'] = key;
+ attrs = {
+ options: options,
+ groups: groups,
+ encodeSpaces: encodeSpaces,
+ encode: encode,
+ strict: strict,
+ };
+ content = this.renderSelectOptions(selection, value, attrs);
+ lines.push(this.tag('optgroup', '\n' + content + '\n', groupAttrs));
+ } else {
+ attrs = options[key] !== undefined ? options[key] : {};
+ attrs['value'] = key as string;
+ console.log('attrs.selected', options);
+ if (attrs.selected === undefined && selection !== undefined && !DataHelper.strcmp(key, selection)) {
+ attrs.selected = true;
+ }
+ text = encode ? this.encode(value) : value;
+ if (encodeSpaces) {
+ text = text.replace(' ', ' ');
+ }
+ lines.push(this.tag('option', text, attrs));
+ }
+ }
+
+ return lines.join('\n');
+ }
}
diff --git a/src/orm-adapters/IORMAdapter.ts b/src/orm-adapters/IORMAdapter.ts
index 28766e4..a836171 100644
--- a/src/orm-adapters/IORMAdapter.ts
+++ b/src/orm-adapters/IORMAdapter.ts
@@ -1,10 +1,23 @@
import { ActiveDataProvider, Pagination, Sort } from '..';
import Component from '../base/Component';
import Model from '../base/Model';
-
+/**
+ * Every ORM Adapter created for Pwoli must implement this interface as these are the set of properties and methods
+ * required for Pwoli's ORM related operations.
+ *
+ * @author Mahesh S Warrier
+ */
export default interface IORMAdapter extends Component {
+ /**
+ * The base Model class of the ORM.
+ */
modelClass: Model;
+ /**
+ * This is the object which is used to map the validators in the ORM with the client side validators in Pwoli.
+ * Keys of this object are the validator string for the ORM and the values are the corresponding validators in Pwoli.
+ */
validatorMap: { [key: string]: string };
+
findAll(query: { [key: string]: any }): Promise;
applySort(query: { [key: string]: any }, sort: Sort): { [key: string]: any };
applyPagination(query: { [key: string]: any }, pagination): { [key: string]: any };
diff --git a/src/orm-adapters/ORMAdapter.ts b/src/orm-adapters/ORMAdapter.ts
index db4cb0e..1ad78f6 100644
--- a/src/orm-adapters/ORMAdapter.ts
+++ b/src/orm-adapters/ORMAdapter.ts
@@ -1,7 +1,9 @@
-import Pwoli from '../base/Pwoli';
import Application from '../base/Application';
import Component from '../base/Component';
-import IORMAdapter from './IORMAdapter';
+/**
+ * ORMAdapter should be the base class for all ORM Adapters(like [[SequelizeAdapter]]) in Pwoli.
+ * It serves as the interface between Pwoli and the ORM being used.
+ */
export default class ORMAdapter extends Component {
public modelClass;
diff --git a/src/orm-adapters/SequelizeAdapter.ts b/src/orm-adapters/SequelizeAdapter.ts
index 517a1c9..642ae52 100644
--- a/src/orm-adapters/SequelizeAdapter.ts
+++ b/src/orm-adapters/SequelizeAdapter.ts
@@ -3,8 +3,13 @@ import Model from '../base/Model';
import Sort from '../data/Sort';
import IORMAdapter from './IORMAdapter';
import ORMAdapter from './ORMAdapter';
-
+/**
+ * SequelizeAdapter is the ORM connectivity interface between Pwoli and the Sequelize ORM.
+ * @see [Using a different ORM](/pwoli/using-another-orm)
+ * @author Mahesh S Warrier
+ */
export default class SequelizeAdapter extends ORMAdapter implements IORMAdapter {
+ /** @inheritdoc */
public validatorMap: { [key: string]: string } = {
is: 'regex',
not: 'regexInverse',
@@ -17,7 +22,6 @@ export default class SequelizeAdapter extends ORMAdapter implements IORMAdapter
Object.assign(this, config);
}
- /** seeds property comment */
public async findAll(query: { [key: string]: any }): Promise {
return await this.modelClass.findAll(query);
}
@@ -49,10 +53,8 @@ export default class SequelizeAdapter extends ORMAdapter implements IORMAdapter
public setAttributes(model: Model, values: { [key: string]: any }): Model {
for (let attribute in values) {
if (values[attribute] !== undefined) {
- if(model.rawAttributes[attribute] !== undefined)
- model[attribute] = values[attribute];
- else
- model.dataValues[attribute] = values[attribute];
+ if (model.rawAttributes[attribute] !== undefined) model[attribute] = values[attribute];
+ else model.dataValues[attribute] = values[attribute];
}
}
return model;