Recursively build an object based on the keys & values from another? - javascript

I want to programmatically build a dynamic data object based on a context object, like in the example below.
const context = {
...other props...,
groups: [
{
heading: 'basic',
canHaveMultiple: false, // data.basic = {username: { type: 'text', value: '' }, password: { type: 'password', value: ''}}
inputs: [
{
name: 'username',
type: 'text',
placeholder: 'username',
},
{
name: 'password',
type: 'password',
placeholder: 'password',
},
],
},
{
heading: 'about',
canHaveMultiple: false, // data.about = {about: { type: 'textarea', value: '' }}
inputs: [
{
name: 'about',
type: 'textarea',
placeholder: 'about',
canHaveMultiple: false,
},
],
},
{
heading: 'hobbies',
canHaveMultiple: true, // data.hobbies = { model: { title: {type: 'text', value: ''}, description: {type: 'textarea', value: ''} }, values: [ { title: {type: 'text', value: ''}, description: {type: 'textarea', value: ''} }]
inputs: [
{
name: 'title',
type: 'text',
placeholder: 'about',
canHaveMultiple: false,
},
{
name: 'description',
type: 'description',
placeholder: null,
canHaveMultiple: false,
},
],
},
{
heading: 'friends',
canHaveMultiple: true, // data.friends = { model: {title: {type: 'text', value: '' }, description: { type: 'textarea', value: '' }} }, values: [{ name: {type: 'text', value: ''},hobbies: [{ title: {type: 'text', value: ''}, description: {type: 'textarea', value: ''}} }] }
inputs: [
{
name: 'name',
type: 'text',
placeholder: 'this is fine',
canHaveMultiple: false
},
{
name: 'hobbies',
type: 'nested',
canHaveMultiple: true,
inputs: [
{
name: 'title',
type: 'textarea',
placeholder: 'about',
canHaveMultiple: false,
},
{
name: 'description',
type: 'textarea',
placeholder: 'about',
canHaveMultiple: false,
},
]
}
],
},
],
}
The output data should be something like so:
data: {
basic: {
username: {
type: 'text',
value: '',
},
password: {
type: 'password',
value: ''
}
},
about: {
about: {
type: 'textarea',
value: '',
}
},
hobbies: {
model: {
title: {
type: 'text',
value: '',
},
description: {
type: 'textarea',
value: '',
}
},
values: [
{
title: {
type: 'text',
value: '',
},
description: {
type: 'textarea',
value: '',
}
}
]
},
friends: {
model: {
name: {
type: 'text',
value: '',
},
hobbies: {
title: {
type: 'text',
value: '',
},
description: {
type: 'textarea',
value: '',
}
}
},
values: [
]
},
}
In essence,
groups[int].heading becomes the top level property of the data object, and
each group along with each of the child from inputs has a canHaveMultiple property, which distinguishes whether the resulting object structure will be either:
canHaveMultiple == false
group.input.name: {
group.type,
value: ''
}
OR
canHaveMultiple == true
{
model: {
group.input.name: {
type: group.input.type,
value: ''
},
... etc
},
values: [{
group.input[0].name: {
type: group.input[0].type,
value: '',
},
group.input[1].name: {
type: group.input[1].type,
value: '',
}
}]
}
The model is there so that I can easily push a new copy of that object into the values array.
So here is my question:
Is there a way to recursively do this so that the program will create the data object from the context object, and keep looking down the context object chain for any 'nested' type (also within the inputs array) until there is none left? Is this do-able and efficient or am I thinking and going about this the wrong way?
*PS: I have been trying real hard and wrecking my head for a few days now on this but I cannot seem to get it working for Nested Objects beyond 3 levels because I am a newbie in recursion. Please help me :(

This function first group by heading (using reduce) then goes recursively over the inputs fields. That is if an input has inputs we loop that too.
const context={groups:[{heading:"basic",canHaveMultiple:!1,inputs:[{name:"username",type:"text",placeholder:"username"},{name:"password",type:"password",placeholder:"password"},]},{heading:"about",canHaveMultiple:!1,inputs:[{name:"about",type:"textarea",placeholder:"about",canHaveMultiple:!1},]},{heading:"hobbies",canHaveMultiple:!0,inputs:[{name:"title",type:"text",placeholder:"about",canHaveMultiple:!1},{name:"description",type:"textarea",placeholder:null,canHaveMultiple:!1},]},{heading:"friends",canHaveMultiple:!0,inputs:[{name:"name",type:"text",placeholder:"this is fine",canHaveMultiple:!1},{name:"hobbies",type:"nested",canHaveMultiple:!0,inputs:[{name:"title",type:"textarea",placeholder:"about",canHaveMultiple:!1},{name:"description",type:"textarea",placeholder:"about",canHaveMultiple:!1},]}]},]}
function transform(arr) {
var result = arr.reduce(function(agg, item) {
var heading = item.heading
var canHaveMultiple = item.canHaveMultiple
var parent;
agg[heading] = {}
if (canHaveMultiple === false) {
parent = agg[heading]
}
if (canHaveMultiple === true) {
agg[heading] = {
model: {},
values: []
}
parent = agg[heading]['model']
}
function do_inputs(parent, inputs) {
inputs.forEach(function(input) {
if (!input.inputs) {
parent[input.name] = {
type: input.type,
value: ''
// todo: placeholder and other properties
}
} else {
// nested
parent[input.name] = {}
do_inputs(parent[input.name], input.inputs)
}
})
}
do_inputs(parent, item.inputs)
return agg;
}, {})
return result;
}
console.log(transform(context.groups));
.as-console-wrapper {
max-height: 100% !important;
}

I have two snippets which might get you most of the way there.
The first one is perhaps too simple, ignoring your model/value part. But it should be easy to understand:
const convert = (xs) => Object .fromEntries (
xs .map (({name, type, inputs = []}) =>
[name, inputs .length ? convert (inputs) : {type, value: ''}]
)
)
const restructure = (context) => ({
data: Object .fromEntries (context .groups .map (
({heading, inputs}) => [heading, convert (inputs)]
))
})
const context = {other: "props", groups: [{heading: "basic", canHaveMultiple: !1, inputs: [{name: "username", type: "text", placeholder: "username"}, {name: "password", type: "password", placeholder: "password"}]}, {heading: "about", canHaveMultiple: !1, inputs: [{name: "about", type: "textarea", placeholder: "about", canHaveMultiple: !1}]}, {heading: "hobbies", canHaveMultiple: !0, inputs: [{name: "title", type: "text", placeholder: "about", canHaveMultiple: !1}, {name: "description", type: "description", placeholder: null, canHaveMultiple: !1}]}, {heading: "friends", canHaveMultiple: !0, inputs: [{name: "name", type: "text", placeholder: "this is fine", canHaveMultiple: !1}, {name: "hobbies", type: "nested", canHaveMultiple: !0, inputs: [{name: "title", type: "textarea", placeholder: "about", canHaveMultiple: !1}, {name: "description", type: "textarea", placeholder: "about", canHaveMultiple: !1}]}]}]}
console .log (restructure (context))
.as-console-wrapper {max-height: 100% !important; top: 0}
The second one does add the model/value part at the expense of some additional complexity. And it's not clear to me if it's entirely correct, although I think it's close:
const convert = (xs) => Object .fromEntries (
xs .map (({name, type, inputs = [], canHaveMultiple}) => [
name,
canHaveMultiple
? {model: convert (inputs), values: [convert (inputs)]}
: inputs.length ? convert (inputs) : {type, value: ''}
])
)
const restructure = (context) => ({
data: Object .fromEntries (context .groups .map (
({heading, inputs, canHaveMultiple}) => [
heading,
canHaveMultiple
? {model: convert (inputs), values: [convert (inputs)]}
: convert (inputs)
]
))
})
const context = {other: "props", groups: [{heading: "basic", canHaveMultiple: !1, inputs: [{name: "username", type: "text", placeholder: "username"}, {name: "password", type: "password", placeholder: "password"}]}, {heading: "about", canHaveMultiple: !1, inputs: [{name: "about", type: "textarea", placeholder: "about", canHaveMultiple: !1}]}, {heading: "hobbies", canHaveMultiple: !0, inputs: [{name: "title", type: "text", placeholder: "about", canHaveMultiple: !1}, {name: "description", type: "description", placeholder: null, canHaveMultiple: !1}]}, {heading: "friends", canHaveMultiple: !0, inputs: [{name: "name", type: "text", placeholder: "this is fine", canHaveMultiple: !1}, {name: "hobbies", type: "nested", canHaveMultiple: !0, inputs: [{name: "title", type: "textarea", placeholder: "about", canHaveMultiple: !1}, {name: "description", type: "textarea", placeholder: "about", canHaveMultiple: !1}]}]}]}
console .log (restructure (context))
.as-console-wrapper {max-height: 100% !important; top: 0}
In both of them, we could simplify a lot if you had a consistent interface. That is, if groups was called inputs and heading was called name, we could consolidate the two repetitive functions into a single one.

Related

Conditionally Filter Options for References in Sanity IO

I have 3 schema,
Menu
Section
Sub-Section
i want to conditionally filter the list of options provided based on the current object. Lets say i want to create a new sub-section which is under Pizza Menu so i will input Menu: Pizza (I created it before) and so section input should only accept and display all the available sections under pizza menu only.
Code:
subSection.js
export default {
name: 'sub-section',
title: 'Sub-Section',
type: 'document',
fields: [
{ name: 'title', type: 'string' },
{ name: 'description', type: 'string' },
{
name: 'menu',
type: 'reference',
to: [{ type: 'menu' }],
},
{
name: 'section',
type: 'reference',
to: [{ type: 'section' }],
options: {
filter: 'menu == references($menu)', // this does not work
filterParams: { menu: 'Neopolitan' }, // ^ ^ This is static, cannot reference current object
},
},
],
};
section.js
export default {
name: 'section',
title: 'Section',
type: 'document',
fields: [
{ name: 'title', type: 'string' },
{ name: 'description', type: 'string' },
{
name: 'menu',
type: 'reference',
to: [{ type: 'menu' }],
},
],
};
menu.js
export default {
title: 'Menu',
name: 'menu',
type: 'document',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
},
],
};

JQuery Query-Builder adding autocomplete plugin

I'm using jquery-querybuilder to build out a query. I'm currently having an issue with adding in selectize as a plugin to allow for autocomplete inside the select inputs. I'm logging the data in the for loop and it prints out the correct data so I know its physically getting the data, but when typing in the input box, there is still no autocomplete and I'm not quite sure where I went wrong.
let totalMachines = [];
var rules_basic = {
condition: 'AND',
rules: [{
}, {
condition: 'OR',
rules: [{
}, {
}]
}]
};
let options = {
plugins: [],
allow_empty: true,
filters: [
{
id: 'machineName',
label: 'Machine Name',
type: 'string',
input: 'text',
operators: ['equal'],
plugin: 'selectize',
values: {
},
plugin_config: {
valueField: 'id',
labelField: 'machineName',
searchField: 'machineName',
sortField: 'machineName',
create: false,
maxItems:3,
plugins: ['remove_button'],
onInitialize: function() {
var that = this;
totalMachines.forEach(function(item) {
that.addOption(item);
console.log(item)
});
}
},
valueSetter: function(rule, value) {
rule.$el.find('.rule-value-container input')[0].selectize.setValue(value);
}
},
]
}
$.ajax({
url: '/api-endpoint',
type: 'GET',
contentType: 'application/json',
dataType: 'json',
success: function(response){
console.log(response)
response.forEach((res) => {
totalMachines.push(res[0])
})
console.log(totalMachines)
}
})
.then(() => {
// Fix for Selectize
$('#builder').on('afterCreateRuleInput.queryBuilder', function(e, rule) {
if (rule.filter.plugin == 'selectize') {
rule.$el.find('.rule-value-container').css('min-width', '200px')
.find('.selectize-control').removeClass('form-control');
}
});
$('#builder').queryBuilder(options)
})
It would be extremely helpful if someone could help me figure out how to properly configure this plugin, I've looked at every thread and haven't been able to figure it out.
Here is a simple example, using a local datasource, the namesList array
<script>
$(document).ready(function() {
let namesList = [{ id: '1', name: 'andrew' }, { id: '2', name: 'bob' }, { id: '3', name: 'charles' }, { id: '4', name: 'david' }];
let pluginConfig = {
preload: true,
valueField: 'id',
labelField: 'name',
searchField: 'name',
options: namesList,
items: ['2'],
allowEmptyOption: true,
plugins: ['remove_button'],
onInitialize: function () { },
onChange: function (value) {
console.log(value);
},
valueSetter: function (rule, value) {
rule.$el.find('.rule-value-container input')[0].selectize.setValue(value);
},
valueGetter: function (rule) {
var val = rule.$el.find('.rule-value-container input')[0].selectize.getValue();
return val.split(',');
}
}
let filterList = [{
id: 'age',
type: 'integer',
input: 'text'
},
{
id: 'id',
label: 'name',
name: 'name',
type: 'string',
input: 'text',
plugin: 'selectize',
plugin_config: pluginConfig
}];
let options = {
allow_empty: true,
operators: ['equal', 'not_equal', 'greater', 'greater_or_equal', 'less', 'less_or_equal'],
filters: filterList
}
$('#builder').queryBuilder(options);
// Fix for Selectize
$('#builder').on('afterCreateRuleInput.queryBuilder', function (e, rule) {
if (rule.filter.plugin == 'selectize') {
rule.$el.find('.rule-value-container').css('min-width', '200px').find('.selectize-control').removeClass('form-control');
}
});
});

Creating a new object using reduce in JS

I've been dealing with creating an object with reduce from a nested array.
The object needs to have the _iud keys with the value of initialValue, from the object that contains both keys. I made a function that can iterate through them but can't return the new object with all the new properties I got. I've tried to use the shallow and deep copy to clone acc but can't make it :( Could anyone give me a hand?
This is the nested array
export const formData = [
{
component: 'page',
label: 'Page 1',
_uid: '0c946643-5a83-4545-baea-055b27b51e8a',
fields: [
{
component: 'field_group',
label: 'Name',
_uid: 'eb169f76-4cd9-4513-b673-87c5c7d27e02',
fields: [
{
component: 'text',
label: 'First Name',
initialValue: '2345432',
type: 'text',
_uid: '5b9b79d2-32f2-42a1-b89f-203dfc0b6b98',
},
{
component: 'text',
label: 'Last Name',
initialValue: '2345432',
type: 'text',
_uid: '6eff3638-80a7-4427-b07b-4c1be1c6b186',
},
],
},
{
component: 'text',
label: 'Email',
initialValue: '2345432',
type: 'email',
_uid: '7f885969-f8ba-40b9-bf5d-0d57bc9c6a8d',
},
{
component: 'text',
label: 'Phone',
initialValue: '2345432',
type: 'text',
_uid: 'f61233e8-565e-43d0-9c14-7d7f220c6020',
},
],
},
{
component: 'page',
label: 'Page 2',
_uid: '3a30803f-135f-442c-ab6e-d44d7d7a5164',
fields: [
{
component: 'options',
label: 'Radio Buttons',
type: 'radio',
initialValue: '2345432',
_uid: 'bd90f44a-d479-49ae-ad66-c2c475dca66b',
options: [
{
component: 'option',
label: 'Option 1',
value: 'one',
},
{
component: 'option',
label: 'Option 2',
value: 'two',
},
],
},
{
component: 'text',
label: 'Conditional Field',
type: 'text',
_uid: 'bd90f44a-d479-49ae-ad66-c2c475daa66b',
initialValue: '2345432',
conditional: {
value: 'two',
field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
},
],
},
{
component: 'page',
label: 'Page 3a',
_uid: 'cd392929-c62e-4cdb-b4dd-914035c1cc8d',
initialValue: '2345432',
conditional: {
value: 'one',
field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
fields: [
{
component: 'options',
label: 'More radio buttons',
type: 'radio',
_uid: 'a15bef56-ab67-4b98-a781-4441cc3bba56',
initialValue: '2345432',
options: [
{ component: 'option', label: 'Option 1', value: 'one' },
{ component: 'option', label: 'Option 2', value: 'two' },
],
},
],
},
{
component: 'page',
label: 'Page 3b',
_uid: '1dd4ec7c-fb53-47f4-af1b-1ab8f805b888',
conditional: {
value: 'two',
initialValue: '2345432',
field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
fields: [
{
component: 'options',
label: 'Something to toggle',
type: 'radio',
_uid: '3ca9237d-e225-4950-a298-f81ce996cb85',
options: [
{
component: 'option',
label: 'Option 1',
value: 'one',
},
{ component: 'option', label: 'Option 2', value: 'two' },
],
},
{
component: 'field_group',
label: 'Name',
_uid: 'b8406cb5-ff0d-4a83-a8f8-99740b6d91f7',
fields: [
{
component: 'text',
label: 'First Name',
initialValue: '2345432',
type: 'text',
_uid: 'c6e065e1-dbcb-44ea-831f-ac3af889e964',
},
{
component: 'text',
label: 'Last Name',
initialValue: '2345432',
type: 'text',
_uid: 'e279ba9c-3c9b-4df8-b267-d14b3c2adcdd',
},
],
},
{
component: 'text',
label: 'Email',
initialValue: '2345432',
type: 'email',
_uid: 'a95208a0-7673-48a8-b704-2fb408fa6eec',
},
{
component: 'text',
label: 'Phone',
initialValue: '2345432',
type: 'text',
_uid: '8dde5083-0619-42d6-8fc7-0563c35d03ad',
},
],
},
{
component: 'page',
label: 'Page 4',
_uid: '0c946643-5a83-4545-baea-065b27b51e8a',
fields: [
{
component: 'text',
label: 'Final Comment',
initialValue: '2345432',
type: 'text',
_uid: 'f61231e8-565e-43d0-9c14-7d7f220c6020',
},
],
},
]
The function:
function getInitialValues(formData = []) {
return Array.from(formData).reduce((acc, currentValue, idx) => {
const arrayOfStrings = ['page', 'field_group', 'options']
const str = currentValue.component
const found = arrayOfStrings.find((v) => str === v)
if (found) {
// console.log('entro', currentValue)
getInitialValues(currentValue?.fields)
return acc
}
if (
(currentValue.component === 'text' || currentValue.component === 'select') &&
currentValue.label === 'Conditional Field'
) {
acc[currentValue._uid] = currentValue?.initialValue
return acc
}
return acc
}, {})
}
My expected result is something like this from all the _uids that has initialValues (only for references)
{
5b9b79d2-32f2-42a1-b89f-203dfc0b6b98: '2345432',
5b9b79d2-32f2-42a1-b89f-203dfc0b6b97: '2345431',
5b9b79d2-32f2-42a1-b89f-203dfc0b6b96: '2345430',
}
I find it easier to separate the extraction of your _uid/initialValue properties from the formatting of these into an output object.
Here is a recursive extract function and a simple convert function that builds on it.
const extract = (xs) =>
xs .flatMap (({initialValue, _uid, fields = []}) =>
[[_uid, initialValue], ...extract (fields)]
) .filter (([_, init]) => init != null)
const convert = xs =>
Object .fromEntries (extract (xs))
const formData = [{component: "page", label: "Page 1", _uid: "0c946643-5a83-4545-baea-055b27b51e8a", fields: [{component: "field_group", label: "Name", _uid: "eb169f76-4cd9-4513-b673-87c5c7d27e02", fields: [{component: "text", label: "First Name", initialValue: "2345432", type: "text", _uid: "5b9b79d2-32f2-42a1-b89f-203dfc0b6b98"}, {component: "text", label: "Last Name", initialValue: "2345432", type: "text", _uid: "6eff3638-80a7-4427-b07b-4c1be1c6b186"}]}, {component: "text", label: "Email", initialValue: "2345432", type: "email", _uid: "7f885969-f8ba-40b9-bf5d-0d57bc9c6a8d"}, {component: "text", label: "Phone", initialValue: "2345432", type: "text", _uid: "f61233e8-565e-43d0-9c14-7d7f220c6020"}]}, {component: "page", label: "Page 2", _uid: "3a30803f-135f-442c-ab6e-d44d7d7a5164", fields: [{component: "options", label: "Radio Buttons", type: "radio", initialValue: "2345432", _uid: "bd90f44a-d479-49ae-ad66-c2c475dca66b", options: [{component: "option", label: "Option 1", value: "one"}, {component: "option", label: "Option 2", value: "two"}]}, {component: "text", label: "Conditional Field", type: "text", _uid: "bd90f44a-d479-49ae-ad66-c2c475daa66b", initialValue: "2345432", conditional: {value: "two", field: "3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b"}}]}, {component: "page", label: "Page 3a", _uid: "cd392929-c62e-4cdb-b4dd-914035c1cc8d", initialValue: "2345432", conditional: {value: "one", field: "3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b"}, fields: [{component: "options", label: "More radio buttons", type: "radio", _uid: "a15bef56-ab67-4b98-a781-4441cc3bba56", initialValue: "2345432", options: [{component: "option", label: "Option 1", value: "one"}, {component: "option", label: "Option 2", value: "two"}]}]}, {component: "page", label: "Page 3b", _uid: "1dd4ec7c-fb53-47f4-af1b-1ab8f805b888", conditional: {value: "two", initialValue: "2345432", field: "3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b"}, fields: [{component: "options", label: "Something to toggle", type: "radio", _uid: "3ca9237d-e225-4950-a298-f81ce996cb85", options: [{component: "option", label: "Option 1", value: "one"}, {component: "option", label: "Option 2", value: "two"}]}, {component: "field_group", label: "Name", _uid: "b8406cb5-ff0d-4a83-a8f8-99740b6d91f7", fields: [{component: "text", label: "First Name", initialValue: "2345432", type: "text", _uid: "c6e065e1-dbcb-44ea-831f-ac3af889e964"}, {component: "text", label: "Last Name", initialValue: "2345432", type: "text", _uid: "e279ba9c-3c9b-4df8-b267-d14b3c2adcdd"}]}, {component: "text", label: "Email", initialValue: "2345432", type: "email", _uid: "a95208a0-7673-48a8-b704-2fb408fa6eec"}, {component: "text", label: "Phone", initialValue: "2345432", type: "text", _uid: "8dde5083-0619-42d6-8fc7-0563c35d03ad"}]}, {component: "page", label: "Page 4", _uid: "0c946643-5a83-4545-baea-065b27b51e8a", fields: [{component: "text", label: "Final Comment", initialValue: "2345432", type: "text", _uid: "f61231e8-565e-43d0-9c14-7d7f220c6020"}]}]
console .log (convert (formData))
.as-console-wrapper {max-height: 100% !important; top: 0}
The question also seems to imply that you want to do an update of the data. I would think of that as a separate step, which you already seem to be able to do.
The problem is in:
if (found) {
// console.log('entro', currentValue)
getInitialValues(currentValue?.fields)
return acc
}
this call to getInitialValues is supposed to return a result that is not saved into acc.
One way to correct it is to do something like this:
if (found) {
const res = getInitialValues(currentValue.fields);
return Object.assign(acc, res);
}
There may be other issues as well, but this is the one that stood up for me.
Note: two out of three of the uids in the "expected results" do not appear in the first code snippet (the example of formData).
As for the other one (with uid: 5b9b79d2-32f2-42a1-b89f-203dfc0b6b98) it has a label of 'First Name' not 'Conditional Field' - that's why it doesn't show up in the output.
(Full) Corrected code
function getInitialValues (formData = []) {
return formData.reduce((acc, currentValue) => {
const arrayOfStrings = ['page', 'field_group', 'options'];
const str = currentValue.component;
const found = arrayOfStrings.find((v) => str === v);
if (found) {
const res = getInitialValues(currentValue.fields);
return Object.assign(acc, res);
}
if (
(currentValue.component === 'text' || currentValue.component === 'select') &&
currentValue.label === 'Conditional Field'
) {
if (currentValue.initialValue) {
acc[currentValue._uid] = currentValue.initialValue;
}
}
return acc;
}, {});
I found a simple forEach loop with recursion to be easier.
const formData = [{
component: 'page',
label: 'Page 1',
_uid: '0c946643-5a83-4545-baea-055b27b51e8a',
fields: [{
component: 'field_group',
label: 'Name',
_uid: 'eb169f76-4cd9-4513-b673-87c5c7d27e02',
fields: [{
component: 'text',
label: 'First Name',
initialValue: '2345432',
type: 'text',
_uid: '5b9b79d2-32f2-42a1-b89f-203dfc0b6b98',
},
{
component: 'text',
label: 'Last Name',
initialValue: '2345432',
type: 'text',
_uid: '6eff3638-80a7-4427-b07b-4c1be1c6b186',
},
],
},
{
component: 'text',
label: 'Email',
initialValue: '2345432',
type: 'email',
_uid: '7f885969-f8ba-40b9-bf5d-0d57bc9c6a8d',
},
{
component: 'text',
label: 'Phone',
initialValue: '2345432',
type: 'text',
_uid: 'f61233e8-565e-43d0-9c14-7d7f220c6020',
},
],
},
{
component: 'page',
label: 'Page 2',
_uid: '3a30803f-135f-442c-ab6e-d44d7d7a5164',
fields: [{
component: 'options',
label: 'Radio Buttons',
type: 'radio',
initialValue: '2345432',
_uid: 'bd90f44a-d479-49ae-ad66-c2c475dca66b',
options: [{
component: 'option',
label: 'Option 1',
value: 'one',
},
{
component: 'option',
label: 'Option 2',
value: 'two',
},
],
},
{
component: 'text',
label: 'Conditional Field',
type: 'text',
_uid: 'bd90f44a-d479-49ae-ad66-c2c475daa66b',
initialValue: '2345432',
conditional: {
value: 'two',
field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
},
],
},
{
component: 'page',
label: 'Page 3a',
_uid: 'cd392929-c62e-4cdb-b4dd-914035c1cc8d',
initialValue: '2345432',
conditional: {
value: 'one',
field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
fields: [{
component: 'options',
label: 'More radio buttons',
type: 'radio',
_uid: 'a15bef56-ab67-4b98-a781-4441cc3bba56',
initialValue: '2345432',
options: [{
component: 'option',
label: 'Option 1',
value: 'one'
},
{
component: 'option',
label: 'Option 2',
value: 'two'
},
],
}, ],
},
{
component: 'page',
label: 'Page 3b',
_uid: '1dd4ec7c-fb53-47f4-af1b-1ab8f805b888',
conditional: {
value: 'two',
initialValue: '2345432',
field: '3a30803f-135f-442c-ab6e-d44d7d7a5164_bd90f44a-d479-49ae-ad66-c2c475dca66b',
},
fields: [{
component: 'options',
label: 'Something to toggle',
type: 'radio',
_uid: '3ca9237d-e225-4950-a298-f81ce996cb85',
options: [{
component: 'option',
label: 'Option 1',
value: 'one',
},
{
component: 'option',
label: 'Option 2',
value: 'two'
},
],
},
{
component: 'field_group',
label: 'Name',
_uid: 'b8406cb5-ff0d-4a83-a8f8-99740b6d91f7',
fields: [{
component: 'text',
label: 'First Name',
initialValue: '2345432',
type: 'text',
_uid: 'c6e065e1-dbcb-44ea-831f-ac3af889e964',
},
{
component: 'text',
label: 'Last Name',
initialValue: '2345432',
type: 'text',
_uid: 'e279ba9c-3c9b-4df8-b267-d14b3c2adcdd',
},
],
},
{
component: 'text',
label: 'Email',
initialValue: '2345432',
type: 'email',
_uid: 'a95208a0-7673-48a8-b704-2fb408fa6eec',
},
{
component: 'text',
label: 'Phone',
initialValue: '2345432',
type: 'text',
_uid: '8dde5083-0619-42d6-8fc7-0563c35d03ad',
},
],
},
{
component: 'page',
label: 'Page 4',
_uid: '0c946643-5a83-4545-baea-065b27b51e8a',
fields: [{
component: 'text',
label: 'Final Comment',
initialValue: '2345432',
type: 'text',
_uid: 'f61231e8-565e-43d0-9c14-7d7f220c6020',
}, ],
},
]
function getInitialValues(formData = [], output=[]) {
formData.forEach(obj => {
// first check for the Conditional Field label
let found = obj.hasOwnProperty('label') && obj.label === "Conditional Field";
// then loop through the component fields if neccesary
if (!found) ['select','text','options'].forEach(val => {
if (obj.hasOwnProperty('component') && obj.component === val) found = true;})
// if found update the output array
if (found) output.push({[obj._uid]: obj.initialValue})
// check for recursion
if (obj.hasOwnProperty('fields')) output = getInitialValues(obj.fields, output)
})
return output;
}
console.log(getInitialValues(formData))

JSGrid add icon instead of text based on true or false value

I am trying to add an icon(a lock) based on whether a value is true or false in a JSGrid.
I have a variable called SoftLock, and if this is true I want to insert a lock icon on the grid.
I have the following fields but am unsure about how to continue:
var fields = [
{ name: 'ID', type: 'text', visible: false },
//THIS FIELD BELOW
{ name: 'SoftLock', type: 'text', title: 'Locked', formatter : function () {return "<span class='fa fa-lock'><i class='fa fa-lock' aria-hidden='true'></i></span>"} },
//THIS FIELD ABOVE
{ name: 'Status', type: 'select', items: MatterStatusEnum.List, valueField: 'Id', textField: 'Name', width: 70, title: 'Account Status' },
{ name: 'AttorneyRef', type: 'text', title: 'Reference' },
{ name: 'Investors', type: 'text', title: 'Investor/s' },
{ name: 'AccountNumber', type: 'text', width: 70, title: 'Account Number' },
{ name: 'IntermediaryName', type: 'text', title: 'Intermediary Name' },
{ name: 'CreatedBy', type: 'text', title: 'Captured By' },
{ name: 'RequestedDate', type: 'date', title: 'Requested Date'}
];
I have used the formatter with no luck. Also, how can I show an icon if true, and nothing if false.
Any help would be appreciated.
I solved this by using the itemTemplate as follows:
{
name: 'SoftLock', type: 'text', title: 'Locked', width: 30,
itemTemplate : function (value, item) {
var iconClass = "";
if (value == true) {
iconClass = "fa fa-lock"; //this is my class with an icon
}
return $("<span>").attr("class", iconClass);
}
Simple as that :)
Much later but try the following
{
type: "control",
editButton: true
}
Also the answer is better described in the formal documentation.
http://js-grid.com/docs/#control

Extjs 5, data model association & load nested data

trying to make this work....
I want to load nested data on two object model
Ext.application({
name : 'MyApp',
launch : function() {
Ext.define('MyApp.model.Address', {
extend: 'Ext.data.Model',
entityName: 'Address',
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'addressLine',
type: 'string'
},
{
name: 'city',
type: 'string'
},
{
name: 'created',
type: 'date',
dateFormat: 'time',
persist: false
}
]
});
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
entityName: 'User',
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'address',
reference: 'Address'
},
{
name: 'name',
type: 'string'
},
{
name: 'lastname',
type: 'string'
},
{
name: 'created',
type: 'date',
dateFormat: 'time',
persist: false
}
]
});
var user = new MyApp.model.User({
"id": 1,
"name": "Pedro",
"lastname": "Carbonell",
"address": {
"id": 1,
"addressLine": "Bailen 22",
"city": "Barcelona",
"created": 1420668866000
},
"created": 1420668866000
});
console.info(user);
console.info(user.getAddress());
}});
It's result on no error when created the user, but when I access to associated data via user.getAddress() it returned an exception:
Uncaught Error: The model ID configured in data ("[object Object]") has been rejected by the int field converter for the id fieldext-all-debug.js
Try to define proxy like memory or localstorage on model definitions, but the result it is the same.
Ext fiddle: https://fiddle.sencha.com/#fiddle/h2d
Any help will be appreciated!
Solved, but only find this solution: when use loadRawData...
var store = new Ext.data.Store({
model: MyApp.model.User
});
store.loadRawData({
"id": 1,
"name": "Pedro",
"lastname": "Carbonell",
"address": {
"id": 1,
"addressLine": "Bailen 22",
"city": "Barcelona",
"created": 1420668866000
},
"created": 1420668866000
});
console.info(store.first());
console.info(store.first().getAddress());
sample at this new fiddle: https://fiddle.sencha.com/#fiddle/h4e
you'r right, ext is a bit flaky, very....
I've been playing around with the code in your fiddle and not been able to get the association working the official way as of yet.
I simulated the functionality using this code:
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'int'
}, {
name: 'name',
type: 'string'
}, {
name: 'lastname',
type: 'string'
}, {
name: 'created',
type: 'date',
dateFormat: 'time',
persist: false
}],
getAddress: function() {
if ('undefined' === this.data.address) {
return null;
}
return Ext.create('Address', this.data.address);
}
});
Basically I've removed the association and created a custom function to create a model record based off of the raw data passed in, You could also return a new, empty model if the address data does not exist instead of null, I used null as it's easier to determine whether you have a valid address record or not.
As already mentioned - this is not the official way to do this, I will have another play around with the fiddle and post a better solution once I find it, this may help in the meantime.
Using the original code, I made a few modifications and now it appears to be working.
Ext.application({
name : 'MyApp',
launch : function() {
Ext.define('MyApp.model.Address', {
extend: 'Ext.data.Model',
//entityName: 'Address',
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'addressLine',
type: 'string'
},
{
name: 'city',
type: 'string'
},
{
name: 'created',
type: 'date',
dateFormat: 'time',
persist: false
}
],
hasMany: 'User'
});
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
//entityName: 'User',
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'name',
type: 'string'
},
{
name: 'lastname',
type: 'string'
},
{
name: 'created',
type: 'date',
dateFormat: 'time',
persist: false
}
],
hasMany: { model: 'Address', name: 'Address' }
});
var user = new MyApp.model.User({
"id": 1,
"name": "Pedro",
"lastname": "Carbonell",
"address": {
"id": 1,
"addressLine": "Bailen 22",
"city": "Barcelona",
"created": 1420668866000
},
"created": 1420668866000
});
console.info(user);
console.info(user.data.address);
}
});
Is this the sort of thing you're after? I set Address manually on the User model. Not ideal but it's interpreted correctly as a record then.
Ext.define('MyApp.model.Address', {
extend: 'Ext.data.Model',
entityName: 'Address',
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'addressLine',
type: 'string'
},
{
name: 'city',
type: 'string'
},
{
name: 'created',
type: 'date',
dateFormat: 'time',
persist: false
}
]
});
Ext.define('MyApp.model.User', {
extend: 'Ext.data.Model',
entityName: 'User',
fields: [
{
name: 'id',
type: 'int'
},
{
name: 'addressId',
reference: 'Address'
},
{
name: 'name',
type: 'string'
},
{
name: 'lastname',
type: 'string'
},
{
name: 'created',
type: 'date',
dateFormat: 'time',
persist: false
}
]
});
var user = new MyApp.model.User({
"id": 1,
"name": "Pedro",
"lastname": "Carbonell",
"created": 1420668866000
});
var addr = new MyApp.model.Address({
"id": 1,
"addressLine": "Bailen 22",
"city": "Barcelona",
"created": 1420668866000
});
user.setAddress(addr);
console.info(user);
console.info(user.getAddress());

Categories

Resources