Skip to content

Feature/Add Form support for containsMany field using MultiLine #784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 47 commits into from
Sep 19, 2019
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
64c4287
init
ibelar Jun 26, 2019
b61d508
Apply fixes from StyleCI
romaninsh Jun 26, 2019
80c3178
something
DarkSide666 Jul 9, 2019
9025168
Apply fixes from StyleCI
romaninsh Jul 9, 2019
c65ee76
Merge branch 'develop' into test/multiline-containsmany
ibelar Jul 22, 2019
e21526d
feature/Add Form support for containsMany field using MultiLine
ibelar Jul 23, 2019
14242b4
Apply fixes from StyleCI
romaninsh Jul 23, 2019
1cd3228
update test file
ibelar Jul 23, 2019
426c2d7
delete test file
ibelar Jul 23, 2019
7f14fd7
remove db file
ibelar Jul 23, 2019
5f956ee
Merge remote-tracking branch 'origin/develop' into feature/multine-co…
romaninsh Jul 23, 2019
4df3629
re-added .sql file
romaninsh Jul 23, 2019
7560c09
update template
ibelar Jul 23, 2019
b15334d
add support for row limit
ibelar Jul 23, 2019
7c6b978
Apply fixes from StyleCI
romaninsh Jul 23, 2019
46bd96d
add support for dropdown
ibelar Jul 24, 2019
23ff75b
Apply fixes from StyleCI
romaninsh Jul 24, 2019
ccd35bd
fix
ibelar Jul 25, 2019
6031abb
add caption
ibelar Jul 25, 2019
3295cb3
remove console.log
ibelar Jul 25, 2019
e23ef32
allow to specify options for supported field type
ibelar Jul 25, 2019
107b877
update option property name
ibelar Jul 25, 2019
1e7991e
Update Mapping of fields into desired JS Format
PhilippGrashoff Aug 14, 2019
64e8ab6
Apply fixes from StyleCI
romaninsh Aug 14, 2019
d9d8705
fix js using new options
ibelar Aug 15, 2019
3b68650
Apply fixes from StyleCI
romaninsh Aug 15, 2019
9b89ebe
fix defining option
ibelar Aug 15, 2019
0c3d126
Merge branch 'develop' into feature/multine-containsmany
ibelar Aug 15, 2019
fd3a242
Merge branch 'develop' into feature/multine-containsmany
ibelar Aug 15, 2019
9980e55
enhanced
ibelar Aug 15, 2019
a19abce
code review
ibelar Aug 16, 2019
871e979
First scratch for multiline Documentation
PhilippGrashoff Aug 16, 2019
a5ecdbf
add semantic-ui-vue in atkjs bundle
ibelar Aug 16, 2019
9ccca5f
update webpack configuration
ibelar Aug 16, 2019
367ff44
Better draft, formatting and Screenshots follow
PhilippGrashoff Aug 16, 2019
6526efe
Add some formatting
PhilippGrashoff Aug 16, 2019
df6620c
Update Formatting
PhilippGrashoff Aug 16, 2019
c0c832a
Add Multiline Screenshots
PhilippGrashoff Aug 16, 2019
e5c91c6
Fix Link to Screenshot
PhilippGrashoff Aug 16, 2019
bb7462e
Added Expressions, onLineChange()
PhilippGrashoff Aug 22, 2019
f45d538
Chapter "Changing appearance"
PhilippGrashoff Aug 22, 2019
a9a8a01
Improve formatting
PhilippGrashoff Aug 22, 2019
b4a501c
update doc
ibelar Aug 23, 2019
1577740
add demo
DarkSide666 Sep 5, 2019
6d4da94
Apply fixes from StyleCI
romaninsh Sep 5, 2019
126abef
minor fix
ibelar Sep 9, 2019
1e38da6
remove useSuiVue
ibelar Sep 9, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed demos/atk4.mwb
Binary file not shown.
8 changes: 4 additions & 4 deletions demos/multiline.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ public function init()
parent::init();

$this->addField('item', ['required' => true, 'default' => 'item']);
$this->addField('qty', ['type' => 'number', 'caption' => 'Qty / Box', 'required' => true, 'ui' => ['multiline' => ['width' => 2]]]);
$this->addField('box', ['type' => 'number', 'caption' => '# of Boxes', 'required' => true, 'ui' => ['multiline' => ['width' => 2]]]);
$this->addField('qty', ['type' => 'integer', 'caption' => 'Qty / Box', 'required' => true, 'ui' => ['multiline' => ['width' => 2]]]);
$this->addField('box', ['type' => 'integer', 'caption' => '# of Boxes', 'required' => true, 'ui' => ['multiline' => ['width' => 2]]]);
$this->addExpression('total', ['expr' => function ($row) {
return $row['qty'] * $row['box'];
}, 'type' => 'number']);
}, 'type' => 'integer']);
}
}

$app->add(['Header', 'MultiLine form field', 'icon' => 'database', 'subHeader' => 'Collect/Edit multiple rows of table record.']);

$data = [];

$inventory = new InventoryItem(new \atk4\data\Persistence_Array($data));
$inventory = new InventoryItem(new \atk4\data\Persistence\Array_($data));

// Populate some data.
$total = 0;
Expand Down
6 changes: 6 additions & 0 deletions js/Release.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## Release note

### version 1.8.0 (2019-07-23)

- Multiline Vue component now support containsMany / containsOne.
- Multiline Vue component now support limiting number of row data.
- Multiline Vue component now support Dropdown as a field type.

### version 1.7.0

- New atkConfirm plugin. Will display a user confirmaton dialog using fomantic ui modal.
Expand Down
2 changes: 1 addition & 1 deletion js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "atkjs-ui",
"version": "1.7.0",
"version": "1.8.0",
"description": "Agile Toolkit Javascript library.",
"main": "lib/atkjs-ui.js",
"scripts": {
Expand Down
61 changes: 50 additions & 11 deletions js/src/components/multiline.component.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import multilineBody from './multiline/multiline-body.component';
import multilineHeader from './multiline/multiline-header.component';

/**
* MultiLine component.
*
* 2019-07-23 - add support for containsMany.
* - updateLinesField method now return one level data row, {id:4, field1: 'value1'}
* - getInitData method now handle one level data row.
*/
export default {
name: 'atk-multiline',
template: `<div >
<sui-table v-bind="tableProp">
<atk-multiline-header :fields="fieldData" :state="getMainToggleState" :errors="errors"></atk-multiline-header>
<atk-multiline-header :fields="fieldData" :state="getMainToggleState" :errors="errors" :caption="caption"></atk-multiline-header>
<atk-multiline-body :fieldDefs="fieldData" :rowData="rowData" :rowIdField="idField" :deletables="getDeletables" :errors="errors"></atk-multiline-body>
<sui-table-footer>
<sui-table-row>
<sui-table-header-cell/>
<sui-table-header-cell :colspan="getSpan" textAlign="right">
<div is="sui-button-group">
<sui-button size="small" @click.stop.prevent="onAdd" icon="plus" ref="addBtn"></sui-button>
<sui-button size="small" @click.stop.prevent="onAdd" icon="plus" ref="addBtn" :disabled="isLimitReach"></sui-button>
<sui-button size="small" @click.stop.prevent="onDelete" icon="trash" :disabled="isDeleteDisable"></sui-button>
</div>
</sui-table-header-cell>
Expand All @@ -33,6 +40,7 @@ export default {
deletables: [],
hasChangeCb: this.data.hasChangeCb,
errors: {},
caption: this.data.caption ? this.data.caption : null,
tableProp: Object.assign({}, this.tableDefault, this.data.options),
tableDefault : {
basic: false,
Expand All @@ -43,7 +51,7 @@ export default {
stackable: false,
inverted: false,
color: null,
columns: null
columns: null,
}
}
},
Expand Down Expand Up @@ -215,27 +223,48 @@ export default {
*/
updateLinesField: function() {
const field = document.getElementsByName(this.linesField)[0];
field.value = JSON.stringify(this.rowData);

let data = this.rowData.map(item => {
let newItem = {};
for (let i=0; i<item.length; i++) {
const key = Object.keys(item[i])[0];
newItem[key] = Object.values(item[i])[0];
}
return {...newItem}
});

field.value = JSON.stringify(data);
},
/**
* Get initial rowData value.
* We need to compare fields return by model vs what values give us because it could differ.
* For example if a field was add or remove from model after a value was saved. Specially for
* array type field like containsMany / containsOne.
* In other word, rowData must match fields definition.
*
* @returns {Array}
*/
getInitData: function() {
let rows = [], value = '';
// Get field name.
const fields = this.data.fields.map(item => item.field);

// check if input containing data is set and initialized.
let field = document.getElementsByName(this.linesField)[0];
if (field) {
//Map value to our rowData.
rows = JSON.parse(field.value).map(fields => {
let data = Object.keys(fields).map(field => {
return {[field]:fields[field]};
let values = JSON.parse(field.value);
values = Array.isArray(values) ? values : [];

values.forEach(value => {
const data = fields.map(field => {
return {[field]: value[field] ? value[field] : null}
});
data.push({__atkml:this.getUUID()});
return data;
data.push({__atkml: this.getUUID()});
rows.push(data);
});
}

return rows;
},
/**
Expand Down Expand Up @@ -286,12 +315,10 @@ export default {
postData: async function(row) {
let data = {};
const context = this.$refs['addBtn'].$el;
//console.log(context);
let fields = this.fieldData.map( field => field.field);
fields.forEach( field => {
data[field] = row.filter(item => field in item)[0][field];
});
//console.log(data);
data.__atkml_action = 'update-row';
try {
let response = await atk.apiService.suiFetch(this.data.url, {data: data, method: 'post', stateContext:context});
Expand Down Expand Up @@ -344,6 +371,18 @@ export default {
*/
isDeleteDisable() {
return !this.deletables.length > 0;
},
/**
* Check if record limit is reach.
* return false if not.
*
* @returns {boolean}
*/
isLimitReach() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isLimitReached - better language

if (this.data.rowLimit === 0) {
return false;
}
return this.data.rowLimit < this.rowData.length + 1;
}
}
}
13 changes: 9 additions & 4 deletions js/src/components/multiline/multiline-cell.component.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import multilineReadOnly from './multiline-readonly.component';

export default {
name: 'atk-multiline-cell',
template: `
<component :is="fieldType"
:type="inputType"
:fluid="true"
:fluid="true"
class="fluid"
@blur="onBlur"
@input="onInput"
v-model="inputValue"
:readOnlyValue="fieldValue"
:name="fieldName"
ref="cell"><slot></slot></component>
ref="cell"
v-bind="fieldProps"></component>
`,
props: ['cellData', 'fieldType', 'fieldValue'],
components: {
'atk-multiline-readonly': multilineReadOnly,
},
props: ['cellData', 'fieldType', 'fieldValue', 'options', 'fieldProps'],
data() {
return {
field: this.cellData.field,
type: this.cellData.type,
//this field name will not get serialized and sent on form submit.
fieldName: '-'+this.cellData.field,
inputValue: this.fieldValue,
dirtyValue: this.fieldValue,
Expand Down
19 changes: 16 additions & 3 deletions js/src/components/multiline/multiline-header.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export default {
<sui-table-cell :style="{background:'none'}"></sui-table-cell>
<sui-table-cell :style="{background:'none'}" state="error" v-for="(column, idx) in columns" :key="idx" v-if="column.isVisible" :textAlign="getTextAlign(column)"><sui-icon name="attention" v-if="getErrorMsg(column)"></sui-icon>{{getErrorMsg(column)}}</sui-table-cell>
</sui-table-row>
<sui-table-row v-if="hasCaption()">
<sui-table-headerCell :colspan="getVisibleColumns()">{{caption}}</sui-table-headerCell>
</sui-table-row>
<sui-table-row :verticalAlign="'top'">
<sui-table-header-cell width="one" textAlign="center"><input type="checkbox" @input="onToggleDeleteAll" :checked.prop="isChecked" :indeterminate.prop="isIndeterminate" ref="check"></input></sui-table-header-cell>
<sui-table-header-cell v-for="(column, idx) in columns" :key="idx" v-if="column.isVisible" :textAlign="getTextAlign(column)">
Expand All @@ -16,7 +19,7 @@ export default {
</sui-table-row>
</sui-table-header>
`,
props: ['fields', 'state', 'errors'],
props: ['fields', 'state', 'errors', 'caption'],
data() {
return {columns: this.fields, isDeleteAll: false}
},
Expand All @@ -26,7 +29,7 @@ export default {
this.$root.$emit('toggle-delete-all', this.$refs['check'].checked);
});
},
getTextAlign(column) {
getTextAlign: function(column) {
let align = 'left';
if (!column.isEditable) {
switch(column.type) {
Expand All @@ -40,9 +43,19 @@ export default {

return align;
},
hasError() {
getVisibleColumns: function() {
let count = 1; // add deletable column;
this.columns.forEach(field => {
count = field.isVisible ? count + 1 : count;
});
return count;
},
hasError: function() {
return Object.keys(this.errors).length > 0;
},
hasCaption: function() {
return this.caption;
},
getErrorMsg: function(column) {
if (this.hasError()) {
let rows = Object.keys(this.errors);
Expand Down
9 changes: 9 additions & 0 deletions js/src/components/multiline/multiline-readonly.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Simply display a readonly value.
*/

export default {
template: `<div>{{readOnlyValue}}</div>`,
name: 'atk-multiline-readonly',
props: ['readOnlyValue']
}
51 changes: 47 additions & 4 deletions js/src/components/multiline/multiline-row.component.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import multilineCell from './multiline-cell.component'
import multilineCell from './multiline-cell.component';

/**
* A row component.
Expand All @@ -14,13 +14,14 @@ export default {
template: `
<sui-table-row :verticalAlign="'middle'">
<sui-table-cell width="one" textAlign="center"><input type="checkbox" @input="onToggleDelete" v-model="toDelete"></input></sui-table-cell>
<sui-table-cell v-for="(column, idx) in columns" :key="idx" :state="getErrorState(column)" :width="column.width" :style="{overflow: 'visible'}" v-if="column.isVisible" :textAlign="getTextAlign(column)">
<sui-table-cell v-for="(column, idx) in columns" :key="idx" :state="getErrorState(column)" :width="getColumnWidth(column)" :style="{overflow: 'visible'}" v-if="column.isVisible" :textAlign="getTextAlign(column)">
<atk-multiline-cell
:fieldType="getFieldType(column)"
:cellData="column"
@update-value="onUpdateValue"
@post-value="onPostRow"
:fieldValue="getValue(column)">{{getReadOnlyValue(column)}}</atk-multiline-cell>
:fieldValue="getValue(column)"
:fieldProps="getFieldProps(column)"></atk-multiline-cell>
</sui-table-cell>
</sui-table-row>
`,
Expand Down Expand Up @@ -58,6 +59,9 @@ export default {
}
return null;
},
getColumnWidth: function(column) {
return column.fieldOptions ? column.fieldOptions.width : null;
},
onEdit: function () {
this.isEditing = true;
},
Expand Down Expand Up @@ -85,6 +89,42 @@ export default {
});
return temp;
},
/**
* Return component specific props.
* When dropdown is use for example.
*
* @param column
*/
getFieldProps: function(column) {
let props = {};
if (column.type === 'enum') {
const userOptions = column.fieldOptions ? column.fieldOptions.dropdown : {};
const defaultOptions = {
floating : true,
closeOnBlur : true,
openOnFocus : false,
selection: true,
};
props = Object.assign(defaultOptions, userOptions);
props.options = this.getEnumValues(column.values);
}
return props;
},
/**
* Map values for Sui Dropdown.
* Values are possible value for dropdown.
*
* @param values
* @returns {{text: *, value: string, key: string}[]}
*/
getEnumValues: function(values){
if(values) {
return Object.keys(values).map(key => {
return {key: key, value: key, text: values[key]}
});

}
},
/**
* Setup component according to field type.
* For now support input - checkbox.
Expand All @@ -95,11 +135,14 @@ export default {
getFieldType: function (column) {
let type = 'sui-input';
if (!column.isEditable){
type = 'div';
type = 'atk-multiline-readonly';
}
if (column.type === 'boolean') {
type = 'sui-checkbox'
}
if (column.type === 'enum') {
type = 'sui-dropdown';
}
return type;
},
/**
Expand Down
Loading