React-Native - Dynamically generating a form with tcomb-form using an Array of objects - forms

I'm creating a tcomb-form through an array of objects but I don't have a lot of experience with it and honestly I'm struggling a little bit to get hang of it.
This is the array structure that we are going to use:
export const AUDIT_CONTENT =
[
{type: "label", style: "title", group: "4.1", text: "field text here"},
{type: "label", style: "label", group: "4.1", text: "field text here"},
{type: "multi-checkbox", style: "checkbox", group: "4.1", text: "field text here"},
{type: "multi-checkbox", style: "checkbox", group: "4.1", text: "field text here"},
{type: "multi-checkbox", style: "checkbox", group: "4.1", text: "field text here"},
{type: "label", style: "label", group: "4.1", text: "field text here"},
{type: "multi-checkbox", style: "checkbox", group: "4.1", text: "field text here"}
]
The fields with type: label are objects that are going to store fields type: multi-checkbox, and these fields are the ones that are going to be validated. I'm grouping those fields by group, so all fields with group 4.1 are inside an array, the fields with group 4.1 as well and so on.
I managed to dynamically generate those fields by doing the following:
myFields = () => {
for (var c = 0; c < groupedFields.length; c++) {
for (var i = 0; i < groupedFields[c].length; i++ ) {
if (groupedFields[c][i].type === 'multi-checkbox') {
fields[groupedFields[c][i].text] = t.maybe(t.enums({
OPTION_1 : "OPTION 1 Label",
OPTION_2 : "OPTION 2 Label",
OPTION_3 : "OPTION 3 Label",
OPTION_4 : "OPTION 4 Label"
}));
}
}
}
}
var fields = {};
myFields()
var myFormType = t.struct(fields);
Now my problem starts here. I'm only generation the fields that receive a value, in this case the ones with type: multi-checkbox, but, I also want to dynamically render in my form the fields with type: label in the same order as my AUDIT_CONTENT array with those being objects so the result will be something like this:
"Field with style title": {
"Field with style label": [
{"Field with style multi-checkbox": "OPTION_1"},
{"Field with style multi-checkbox": "OPTION_3"},
],
"Other field with style label": [
{"Field with style multi-checkbox": "OPTION_4"},
{"Field with style multi-checkbox": "OPTION_2"},
]
}
This result will be stored in Mongo.
Hope someone can help me out with this and thanks in advance.

It would be better if you provide a visual representation of what you want but i think that you want to render and update a nested structure. For this i recommend recursive map methods for the array.
/*
To render a structure like this you can use map and assign types to the objects to decide what to render
But you should render it all.
Maybe you can use something like this:
*/
renderInputs(array){
return array.map((obj) => {
/* You can generate even nested forms if you want */
if (obj.children) {
return <div> {this.renderInputs()}</div>
} else {
return renderType(obj)
}
})
}
renderType(obj){
switch (obj.type) {
case 'label':
return <Element {...objProps} />
case 'multi-checkbox':
return <Element2 {...objProps} />
/*You even can generate other methods for nested objects*/
case 'other-case':
return <div>{this.OtherSpecialSubRenderMethodIfYoUwANT()}</div>
}
}
/**You will need an recursive method to update your state also and each object is recomended to have an unique id*/
updateState(array = this.state.array, newValue, id){
return array.map((obj) => {
if (obj.children) {
return updateState(obj.children, newValue, id)
}
if (obj.id == id) {
return { ...obj, value: newValue }
}
return obj;
})
}

Related

Ag-grid set column text filter to range of letters

The goal is to set a column's text filter to be a range of letters that values start with. For example in a customer name column setting the filter to be "starts with" and a range of a-m. The user will enter the two letters that define the start and end of the range (eg. "a" and "m").
Ag-grid's filter docs state that "in range" filtering is only supported for date and number data types. Looking at ag-grid's multi-filter example, multiple filters are combined with an OR condition in the filter model:
{
athlete: {
filterType: "multi",
filterModels: [
{
filterType: "text",
operator: "OR",
condition1: {
filterType: "text",
type: "startsWith",
filter: "a"
},
condition2: {
filterType: "text",
type: "startsWith",
filter: "b"
}
},
null
]
}
}
It looks like the solution is to programmatically find all letters in the range specified by the user, then include a condition for each letter in the "filterModels" array. Is there a more efficient way to do this?
A custom filter was the best solution for this scenario.
I was looking to support an optional range of letters, along with optional additional text in the filter field. Using a regular expression that matched this pattern inside the doesFilterPass method is working as expected.
Example using Vue and lodash:
doesFilterPass(params) {
// Regex matches "[A-M]", "[n-z]", "[E-p] additionaltext", etc
const nameFilterRegex = /^(?<nameStartRange>\[[a-z]{1}-[a-z]{1}\])?(?:\s+)?(?<additionalText>[a-z]+)?(?:\s+)?$/i;
const regexResult = nameFilterRegex.exec(params.data.name);
if (!isNil(regexResult)) {
const nameRange = regexResult.groups.nameStartRange;
const additionalText = regexResult.groups.additionalText;
if (!isEmpty(nameRange)) {
try {
const lastNameRegex = new RegExp(nameRange, "gi");
const matchedChar = params.data.name[0].match(lastNameRegex);
if (isEmpty(matchedChar)) {
return false;
}
} catch {
return false;
}
}
if (!isEmpty(additionalText)) {
if (!params.data.filterValue.includes(additionalText)) {
return false;
}
}
}
return true;
};

Get a list of Default Bookmarks

Is there a way to get the default bookmarks with capability APIs?
I tried app.getList('BookmarkList') method, but it doesn't return any parameter identifying that it's a default bookmark.
In order to get the default bookmarks you'll have to create generic object with the following definition:
app.createGenericObject({
qInfo: {
qType: 'sheets'
},
qAppObjectListDef: {
qType: 'sheet',
qData: {
title: '/qMetaDef/title',
labelExpression: '/labelExpression',
description: '/qMetaDef/description',
descriptionExpression: '/descriptionExpression',
thumbnail: '/thumbnail',
cells: '/cells',
actions: '/actions',
rank: '/rank',
columns: '/columns',
rows: '/rows'
}
}
}, sheets => {
console.log(sheets)
})
The key here is the /actions part of the definition which ensures that the actions metadata is received
The resulting layout should include any defined actions, which, for bookmarks will look like this:
{
actionLabel: "A",
actionType: "applyBookmark",
bookmark: "db014c67-ff43-4111-88ff-836b457928e5",
cId: "KzmaWSa",
field: "",
showSystemVariables: false,
softLock: false,
value: "",
variable: ""
}

TinyMCE 5: Autocomplete that can be ignored so you can just type your own entry

I want to help the user author handlebars/mustache templates so when they type a { character, an autocomplete of known template values comes up to assist the user. But the user may not want to choose one of the suggested options, they might just want to continue typing a new template value and terminate it with a }. In the example in the code below, the options "Order Number" and "Delivery Date" are the known autocomplete options, but I want the user to be able to type {Invoice Number} for example.
I've succeeded in make this work in one way. As a user is typing {Invoice Number} I add it to the list of allowed options in the autocompleter fetch function. So the user can get what they want into the document if the click on the suggested option. Clicking fires the onAction function. I want onAction to fire as soon as the user types the closing handlebars character i.e. }.
Here is the code I have tried. I am using TinyMCE 5.
let template = (function () {
'use strict';
tinymce.PluginManager.add("template", function (editor, url) {
const properties = [
{value: "Order Number", text: "Order Number"},
{value: "Delivery Date", text: "Delivery Date"}
];
const insertNewProperty = function(value) {
let property = {value: value, text: value};
properties.push(property);
return property;
};
editor.ui.registry.addAutocompleter('autocompleter-template', {
ch: '{',
minChars: 0,
columns: 1,
fetch: function (pattern) {
return new tinymce.util.Promise(function (resolve) {
let filteredProperties = pattern ? properties.filter(p => p.text.indexOf(pattern) > -1) : properties;
if (filteredProperties.length > 0) {
resolve(filteredProperties);
} else {
resolve([{value: pattern, text: pattern}]);
}
});
},
onAction: function (autocompleteApi, rng, value) {
let property = properties.find(p => p.value === value);
if (!property) {
property = insertNewProperty(value)
}
let content = `{${property.text}}`;
editor.selection.setRng(rng);
editor.insertContent(content);
autocompleteApi.hide();
}
});
return {
getMetadata: function () {
return {
name: "Learning",
url: "https://stackoverflow.com"
};
}
};
});
}());
})();```
You can add a matches function to your callbacks, along with your onAction, this help you do it.
matches: function (rng, text, pattern) {
if(pattern.endsWith('}')){
pattern = pattern.replace('}', '');
/**
* Check if pattern does not match an existing
* variable and do what you want
*/
return true;
}
},
And make sure the function always returns true.

How can I save multiple rows in one mutation in graphql and mongodb?

Here I want to add multiple thoughts in one mutation query.
How can I achieve this:
mutation{
savethoughts(data: [{id:1,name:"a"},data: {id:2,name:"b"}]){
id
}
}
For save multiple records in database you need to follow every steps carefully
For example
mutation{
setMultipleRecord(data: [{title: "first Title", name: "First"},
{title: "Second Title", name: "Second"}])
}
You must have schema type
`
.......
input Record {
title: String!
name: String!
}
.....
`
And add Mutation as given that
type Mutation/type RootMutation { {
......
setMultipleRecord(data: [Record]): String!
}
Here you can see Record is input type schema object where is data parameter holding variable which we are using to get data object.
you can give any name instead of data also change in the argument in mutation
Now in resolver function a
Mutation: {
setMultipleRecord: async(args, { data }) => {
console.log(data)
//Here data contain data which you passed which is
// [{title: "first Title", name: "First"},
// {title: "Second Title", name: "Second"}]
// Perform operation as your requirement
// Please return String
}
}
Now you can change name as par your requirement of object.
You made it successfully...

Adding an item to a cursor MeteorJS

Here is my code right now:
Template.home.helpers({
categories: function(){
// Categories is a collection defined earlier
return Categories.find();
},
});
var categories = Categories.find();
/**
categories.append({
name: "All",
icon: "home"
});
*/
return categories;
},
It is just returning all the categories from the database I am using. I want to make an aggregate category. For example, saw I have 2 categories:
[
{
name: "Link",
views: 5
},
{
name: "Time",
views: 10,
}]
Say I want a 3rd category:
{
name: "All",
views: 15 // this is from 10 + 5
}
How would I go about adding that to the cursor?
Instead of returning a cursor, a helper can also return an array (or a single value). Therefore, you can solve your problem by fetching all of the existing categories into an array, modifying it with the desired data, and returning the modified array. Here is an example implementation:
Template.home.helpers({
categories: function() {
// fetch all of the categories into an array
var cats = Categories.find().fetch();
// compute the total views for all categories
var totalViews = _.reduce(cats, function(memo, cat) {
return memo + cat.views;
}, 0);
// add a new 'category' with the total views
cats.push({name: 'All', views: totalViews});
// return the array of modified categories
return cats;
}
});