Related
I know we can create custom components, but I am curious if there is a way to group a few together into either a Container or a Panel?
For instance, why make everyone create the same 4 fields in a pleasing layout over and over to create a panel for getting a user's Address? Seems like it would be nice to be able to define some buttons on the left that says "Address Form Block", or "Contact Info Block" which then inserts everything in a group.
I am still pretty new to using Formio.JS, so I don't know if this is possible, but I would hope I am not the first to want something like this.
Example form:
Sample Code:
Formio.builder(document.getElementById('builder'), {
"display": "form",
"components": [
{
"title": "Demographics",
"collapsible": false,
"key": "demographics",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [
{
"label": "Columns",
"columns": [
{
"components": [
{
"label": "Last Name",
"tableView": true,
"key": "lastName1",
"type": "textfield",
"input": true
}
],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [
{
"label": "First Name",
"tableView": true,
"key": "lastName",
"type": "textfield",
"input": true
}
],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [
{
"label": "Middle Initial",
"tableView": true,
"key": "middleInitial",
"type": "textfield",
"input": true
}
],
"size": "md",
"width": 2,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 2
}
],
"key": "columns",
"type": "columns",
"input": false,
"tableView": false
},
{
"label": "Birthdate",
"hideInputLabels": false,
"inputsLabelPosition": "top",
"useLocaleSettings": false,
"tableView": false,
"fields": {
"day": {
"hide": false
},
"month": {
"hide": false
},
"year": {
"hide": false
}
},
"key": "birthdate",
"type": "day",
"input": true,
"defaultValue": "00/00/0000"
},
{
"label": "Phone Number",
"tableView": true,
"key": "phoneNumber",
"type": "textfield",
"input": true
}]},
{
"title": "Address",
"collapsible": false,
"key": "address",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [
{
"label": "Street Address",
"tableView": true,
"key": "streetAddress",
"type": "textfield",
"input": true
},
{
"label": "Columns",
"columns": [
{
"components": [
{
"label": "City",
"tableView": true,
"key": "city",
"type": "textfield",
"input": true
}
],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 6
},
{
"components": [
{
"label": "State",
"tableView": true,
"key": "state",
"type": "textfield",
"input": true
}
],
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 3
},
{
"components": [
{
"label": "Zipcode",
"tableView": true,
"key": "zipcode",
"type": "textfield",
"input": true
}
],
"size": "md",
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 3
}
],
"key": "columns1",
"type": "columns",
"input": false,
"tableView": false
}
]
},
{
"label": "Signature",
"tableView": false,
"key": "signature",
"type": "signature",
"input": true
},
{
"type": "button",
"label": "Submit",
"key": "submit",
"disableOnInvalid": true,
"input": true,
"tableView": false
}
]
});
You can use a custom component option like.
const AddressTool = {
"title": "Address",
"collapsible": false,
"key": "address",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [{
"label": "Street Address",
"tableView": true,
"key": "streetAddress",
"type": "textfield",
"input": true
},
{
"label": "Columns",
"columns": [{
"components": [{
"label": "City",
"tableView": true,
"key": "city",
"type": "textfield",
"input": true
}],
"width": 6,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 6
},
{
"components": [{
"label": "State",
"tableView": true,
"key": "state",
"type": "textfield",
"input": true
}],
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 3
},
{
"components": [{
"label": "Zipcode",
"tableView": true,
"key": "zipcode",
"type": "textfield",
"input": true
}],
"size": "md",
"width": 3,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 3
}
],
"key": "columns1",
"type": "columns",
"input": false,
"tableView": false
}
]
};
const DemographicsTool = {
"title": "Demographics",
"collapsible": false,
"key": "demographics",
"type": "panel",
"label": "Panel",
"input": false,
"tableView": false,
"components": [{
"label": "Columns",
"columns": [{
"components": [{
"label": "Last Name",
"tableView": true,
"key": "lastName1",
"type": "textfield",
"input": true
}],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [{
"label": "First Name",
"tableView": true,
"key": "lastName",
"type": "textfield",
"input": true
}],
"width": 5,
"offset": 0,
"push": 0,
"pull": 0,
"size": "md",
"currentWidth": 5
},
{
"components": [{
"label": "Middle Initial",
"tableView": true,
"key": "middleInitial",
"type": "textfield",
"input": true
}],
"size": "md",
"width": 2,
"offset": 0,
"push": 0,
"pull": 0,
"currentWidth": 2
}
],
"key": "columns",
"type": "columns",
"input": false,
"tableView": false
},
{
"label": "Birthdate",
"hideInputLabels": false,
"inputsLabelPosition": "top",
"useLocaleSettings": false,
"tableView": false,
"fields": {
"day": {
"hide": false
},
"month": {
"hide": false
},
"year": {
"hide": false
}
},
"key": "birthdate",
"type": "day",
"input": true,
"defaultValue": "00/00/0000"
},
{
"label": "Phone Number",
"tableView": true,
"key": "phoneNumber",
"type": "textfield",
"input": true
}
]
};
let formioOptions = {
noDefaultSubmitButton: false,
builder: {
basic: false,
advanced: false,
data: false,
premium: false,
layout: false,
custom: {
title: 'Custom Component Set',
weight: 10,
components: {
Demographics: {
title: 'Demographics',
key: 'demographics',
icon: 'terminal',
schema: DemographicsTool
},
Address: {
title: 'Address',
key: 'address',
icon: 'terminal',
schema: AddressTool
}
}
}
}
};
and then call the builder
Formio.builder(document.getElementById('builder'), {
"components": [{
"title": "Test1",
"input": false,
"key": "panel1",
"tableView": false,
"label": "Test 1",
"type": "panel",
"collapsed": false,
"collapsible": true,
"components": [AddressTool, DemographicsTool]
}]
}, formioOptions);
I'm currently working on a personal project, which is an E-Commerce Web App. To do this, I used the Commerce.js library and is currently hitting a roadblock. Although technically I can do this by using nested for loops, I would like to look for a more efficient method that uses cleaner code and is faster and more efficient with resources.
The Commerce.js documentation does not return the products in a certain category, therefore I have to use two functions to get them seperately.
const commerce = new Commerce(process.env.REACT_APP_API_KEY)
var shopItems;
var categoriesList;
useEffect(()=> {
commerce.categories.list().then((categories) => {
categoriesList = categories;
console.log(categoriesList.data)
});
commerce.products.list().then((product) => {
shopItems = product;
console.log(shopItems.data)
});
})
DATA
Categories:
{
"id": "cat_O3bR5XyEklnzdj",
"parent_id": null,
"slug": "new-releases",
"name": "New Releases",
"description": null,
"products": 1,
"created": 1639555008,
"updated": 1639555008,
"meta": null,
"assets": [
{
"id": "ast_Op1YoVxzaglXLv",
"url": "https://cdn.chec.io/merchants/37076/assets/aHVfA98RM9vVKjMa|Screenshot (6).png",
"description": null,
"is_image": true,
"filename": "Screenshot (6).png",
"file_size": 933236,
"file_extension": "png",
"image_dimensions": {
"width": 1920,
"height": 1080
},
"meta": [],
"created_at": 1639555026,
"updated_at": 1639555031
}
],
"children": []
}
Products:
[
{
"id": "prod_RyWOwmdx1GlnEa",
"created": 1639554903,
"updated": 1639554964,
"active": true,
"permalink": "tE45fb",
"name": "Air Jordan 11 Cool Grey",
"description": "<p></p>",
"price": {
"raw": 300000,
"formatted": "300,000.00",
"formatted_with_symbol": "Rp300,000.00",
"formatted_with_code": "300,000.00 IDR"
},
"inventory": {
"managed": false,
"available": 0
},
"sku": null,
"sort_order": 0,
"seo": {
"title": null,
"description": null
},
"thank_you_url": null,
"meta": null,
"conditionals": {
"is_active": true,
"is_tax_exempt": false,
"is_pay_what_you_want": false,
"is_inventory_managed": false,
"is_sold_out": false,
"has_digital_delivery": false,
"has_physical_delivery": false,
"has_images": true,
"collects_fullname": false,
"collects_shipping_address": false,
"collects_billing_address": false,
"collects_extra_fields": false
},
"is": {
"active": true,
"tax_exempt": false,
"pay_what_you_want": false,
"inventory_managed": false,
"sold_out": false
},
"has": {
"digital_delivery": false,
"physical_delivery": false,
"images": true
},
"collects": {
"fullname": false,
"shipping_address": false,
"billing_address": false,
"extra_fields": false
},
"checkout_url": {
"checkout": "https://checkout.chec.io/tE45fb?checkout=true",
"display": "https://checkout.chec.io/tE45fb"
},
"extra_fields": [],
"variant_groups": [],
"categories": [],
"assets": [
{
"id": "ast_8XO3wpM74OoYAz",
"url": "https://cdn.chec.io/merchants/37076/assets/1WUVJv226CmE5Xp2|air-jordan-11-cool-grey-ct8012-005-release-date (1).jpg",
"description": null,
"is_image": true,
"filename": "air-jordan-11-cool-grey-ct8012-005-release-date (1).jpg",
"file_size": 24857,
"file_extension": "jpg",
"image_dimensions": {
"width": 960,
"height": 1080
},
"meta": [],
"created_at": 1639554944,
"updated_at": 1639554948
},
{
"id": "ast_bO6J5aBxDElEjp",
"url": "https://cdn.chec.io/merchants/37076/assets/xRQc9seG948wu332|download (1).jfif",
"description": null,
"is_image": true,
"filename": "download (1).jfif",
"file_size": 7152,
"file_extension": "jpg",
"image_dimensions": {
"width": 294,
"height": 171
},
"meta": [],
"created_at": 1639554948,
"updated_at": 1639554949
},
{
"id": "ast_A12JwrMQExlPjn",
"url": "https://cdn.chec.io/merchants/37076/assets/5RmVeAD6AX6l1O7D|download.jfif",
"description": null,
"is_image": true,
"filename": "download.jfif",
"file_size": 5551,
"file_extension": "jpg",
"image_dimensions": {
"width": 266,
"height": 190
},
"meta": [],
"created_at": 1639554959,
"updated_at": 1639554961
}
],
"image": {
"id": "ast_8XO3wpM74OoYAz",
"url": "https://cdn.chec.io/merchants/37076/assets/1WUVJv226CmE5Xp2|air-jordan-11-cool-grey-ct8012-005-release-date (1).jpg",
"description": null,
"is_image": true,
"filename": "air-jordan-11-cool-grey-ct8012-005-release-date (1).jpg",
"file_size": 24857,
"file_extension": "jpg",
"image_dimensions": {
"width": 960,
"height": 1080
},
"meta": [],
"created_at": 1639554944,
"updated_at": 1639554948
},
"related_products": [],
"attributes": []
},
{
"id": "prod_BkyN5YVzyxl0b6",
"created": 1639564691,
"updated": 1639564756,
"active": true,
"permalink": "BPMQ0E",
"name": "Air Jordan 5 Green Bean",
"description": "",
"price": {
"raw": 400000,
"formatted": "400,000.00",
"formatted_with_symbol": "Rp400,000.00",
"formatted_with_code": "400,000.00 IDR"
},
"inventory": {
"managed": false,
"available": 0
},
"sku": null,
"sort_order": 0,
"seo": {
"title": null,
"description": null
},
"thank_you_url": null,
"meta": null,
"conditionals": {
"is_active": true,
"is_tax_exempt": false,
"is_pay_what_you_want": false,
"is_inventory_managed": false,
"is_sold_out": false,
"has_digital_delivery": false,
"has_physical_delivery": false,
"has_images": true,
"collects_fullname": false,
"collects_shipping_address": false,
"collects_billing_address": false,
"collects_extra_fields": false
},
"is": {
"active": true,
"tax_exempt": false,
"pay_what_you_want": false,
"inventory_managed": false,
"sold_out": false
},
"has": {
"digital_delivery": false,
"physical_delivery": false,
"images": true
},
"collects": {
"fullname": false,
"shipping_address": false,
"billing_address": false,
"extra_fields": false
},
"checkout_url": {
"checkout": "https://checkout.chec.io/BPMQ0E?checkout=true",
"display": "https://checkout.chec.io/BPMQ0E"
},
"extra_fields": [],
"variant_groups": [],
"categories": [
{
"id": "cat_O3bR5XyEklnzdj",
"slug": "new-releases",
"name": "New Releases"
}
],
"assets": [
{
"id": "ast_8XO3wpM7yOoYAz",
"url": "https://cdn.chec.io/merchants/37076/assets/L4NaqCsUMMxS6Ie8|2022-Air-Jordan-5-Green-Bean-1068x707.jpg",
"description": null,
"is_image": true,
"filename": "2022-Air-Jordan-5-Green-Bean-1068x707.jpg",
"file_size": 60899,
"file_extension": "jpg",
"image_dimensions": {
"width": 1068,
"height": 707
},
"meta": [],
"created_at": 1639564675,
"updated_at": 1639564678
}
],
"image": {
"id": "ast_8XO3wpM7yOoYAz",
"url": "https://cdn.chec.io/merchants/37076/assets/L4NaqCsUMMxS6Ie8|2022-Air-Jordan-5-Green-Bean-1068x707.jpg",
"description": null,
"is_image": true,
"filename": "2022-Air-Jordan-5-Green-Bean-1068x707.jpg",
"file_size": 60899,
"file_extension": "jpg",
"image_dimensions": {
"width": 1068,
"height": 707
},
"meta": [],
"created_at": 1639564675,
"updated_at": 1639564678
},
"related_products": [],
"attributes": []
}
]
is there a way to efficiently and cleanly find products based on their categories? Thank you in advance.
Yes. There is a way by using the category 'slug'.
import Commerce from '#chec/commerce.js';
const commerce = new Commerce('{your_public_key}');
// Fetch products specifying a category slug
commerce.products.list({
category_slug: ['shoes'],
}).then(response => response.data);
// Fetch products specifying multiple category slugs
commerce.products.list({
category_slug: ['shoes', 'black'],
}).then(response => response.data);
For more details:
https://commercejs.com/docs/sdk/products#retrieve-product
i have a json returned from an api that shows latest discussions from a forum..
the url that display the json output is like:
https://example.com/api/discussions?isApproved=true&exists=true&sort=-lastPostedAt
and this is the json output:
"links": {
"first": "http://localhost/flarum/public/api/discussions?isApproved=true\u0026exists=true\u0026sort=-lastPostedAt",
"next": "http://localhost/flarum/public/api/discussions?isApproved=true\u0026exists=true\u0026sort=-lastPostedAt\u0026page%5Boffset%5D=20"
},
"data": [
{
"type": "discussions",
"id": "46",
"attributes": {
"title": "footer",
"slug": "46-footer",
"commentCount": 1,
"participantCount": 1,
"createdAt": "2021-09-20T07:14:18+00:00",
"lastPostedAt": "2021-09-20T07:14:18+00:00",
"lastPostNumber": 1,
"canReply": true,
"canRename": true,
"canDelete": true,
"canHide": true,
"lastReadAt": "2021-09-20T07:14:20+00:00",
"lastReadPostNumber": 1,
"isApproved": true,
"canTag": true,
"subscription": null,
"isLocked": false,
"canLock": true
},
"relationships": { "user": { "data": { "type": "users", "id": "1" } }, "lastPostedUser": { "data": { "type": "users", "id": "1" } }, "tags": { "data": [{ "type": "tags", "id": "2" }] } }
},
{
"type": "discussions",
"id": "45",
"attributes": {
"title": "fhgfhfg",
"slug": "45-fhgfhfg",
"commentCount": 2,
"participantCount": 1,
"createdAt": "2021-09-19T16:07:37+00:00",
"lastPostedAt": "2021-09-19T23:04:00+00:00",
"lastPostNumber": 2,
"canReply": true,
"canRename": true,
"canDelete": true,
"canHide": true,
"lastReadAt": "2021-09-19T23:04:02+00:00",
"lastReadPostNumber": 2,
"isApproved": true,
"canTag": true,
"subscription": null,
"isLocked": false,
"canLock": true
},
"relationships": { "user": { "data": { "type": "users", "id": "1" } }, "lastPostedUser": { "data": { "type": "users", "id": "1" } }, "tags": { "data": [{ "type": "tags", "id": "2" }] } }
},
{
"type": "discussions",
"id": "39",
"attributes": {
"title": "Discussion",
"slug": "39-discussion",
"commentCount": 21,
"participantCount": 1,
"createdAt": "2021-05-22T11:06:43+00:00",
"lastPostedAt": "2021-09-04T12:49:32+00:00",
"lastPostNumber": 28,
"canReply": true,
"canRename": true,
"canDelete": true,
"canHide": true,
"lastReadAt": "2021-09-04T12:49:34+00:00",
"lastReadPostNumber": 28,
"isApproved": true,
"canTag": true,
"subscription": null,
"isLocked": false,
"canLock": true
},
"relationships": { "user": { "data": { "type": "users", "id": "1" } }, "lastPostedUser": { "data": { "type": "users", "id": "1" } }, "tags": { "data": [{ "type": "tags", "id": "1" }] } }
}
what i want to do is to have something like "related content" based on a discussion title. For example if i have this title "lets talk about cars", the json must shows all the discussions related to this title.
is this possible?
You can take the data from json and just filter the content inside
const filteredResults = yourJsonResponse.data.filter(item => item.attributes.title === 'lets talk about cars')
This won't be a good idea. as you're getting paginated list with limit(like per page 20/30). if you're using js you can use js filter for getting related content. But as i said. that filer will be done based on your current page data, not all data. it's better to use another api end for related content from large data..
I want to perform the following CRUD operations on this json
1)Update content_available as true for code (EN, CN).
this is what i have tried. but it doesnt work.
myObj = {
"data": [{
"code": "EN",
"language": "English",
"content_available": true,
"isdefault": true
}, {
"code": "AR",
"language": "Arabic",
"content_available": true,
"isdefault": false,
"default" : true
}, {
"code": "BR",
"language": "Brazilian Portuguese",
"content_available": true,
"isdefault": false
}, {
"code": "CN",
"language": "Simplified Chinese",
"content_available": true,
"isdefault": false,
"default" : true
}, {
"code": "TW",
"language": "Traditional Chinese",
"content_available": true,
"isdefault": false
}, {
"code": "DE",
"language": "German",
"content_available": true,
"isdefault": false
}, {
"code": "ES",
"language": "Spanish",
"content_available": true,
"isdefault": false
}, {
"code": "FR",
"language": "French",
"content_available": true,
"isdefault": false
}, {
"code": "JP",
"language": "Japanese",
"content_available": true,
"isdefault": false,
"default" : true
}, {
"code": "RU",
"language": "Russian",
"content_available": false,
"isdefault": false
}],
"success": true
}
function setContentAvailable() {
for (var key in myObj.data) {
if (myObj["data"]["code"] === "EN" && myObj[data][code] === "CN") {
myObj.data.content_available = false;
}
}
}
setContentAvailable();
console.log(myObj);
In your code, a for loop is used but the key is ignored... and the check for EN or CN should be || (or), not && (and).
To Update content_available as true for code (EN, CN), the code should be:
function setContentAvailable() {
for (var key in myObj.data) {
if (myObj["data"][key]["code"] === "EN" || myObj["data"][key]["code"] === "CN") {
myObj["data"][key]["content_available"] = true;
}
}
}
setContentAvailable();
I am relatively new to Knockout.js and have a question regarding the creating of the view model.
What I am currently doing is:
viewModel = {
Menu: ko.observableArray(ko.utils.parseJson(data)),
editMenu: function (menu) {
ko.applyBindings(menu, document.getElementById("MenuCategories"));
$("#MenuCategories").bPopup();
}
There is more than one function, but trying to keep it short.
What I want to do is use the mapping plugin because my json data that I am using contains arrays of objects with each object containing an array and each object in that array also contains an array. What the JSON looks like:
[
{
"Id": 1,
"Name": "Test 5",
"Description": "Testing menu",
"BeveragesMenu": false,
"Active": true,
"Categories": [
{
"Id": 1,
"Name": "Test 1",
"Active": true,
"MenuItems": [
{
"Id": 1,
"Name": "Food",
"Description": "2 Eggs and Bread",
"Price": 50,
"DateBased": false,
"PriceOnRequest": false,
"DateFrom": null,
"DateTo": null,
"Active": true
}
]
},
{
"Id": 2,
"Name": "Test 2",
"Active": true,
"MenuItems": [
]
},
{
"Id": 1004,
"Name": "test",
"Active": true,
"MenuItems": [
]
},
{
"Id": 1005,
"Name": "Test 4",
"Active": true,
"MenuItems": [
]
}
]
},
{
"Id": 92,
"Name": "Test 4",
"Description": "",
"BeveragesMenu": false,
"Active": false,
"Categories": [
{
"Id": 1006,
"Name": "Test 1",
"Active": true,
"MenuItems": [
{
"Name": "Test",
"Description": "",
"Price": "",
"DateBased": false,
"PriceOnRequest": false
}
]
}
]
},
{
"Id": 93,
"Name": "Test 6",
"Description": "",
"BeveragesMenu": false,
"Active": false,
"Categories": [
]
},
{
"Id": 94,
"Name": "Test 9",
"Description": "",
"BeveragesMenu": false,
"Active": false,
"Categories": [
]
},
{
"Id": 95,
"Name": "Test 20]",
"Description": "",
"BeveragesMenu": false,
"Active": false,
"Categories": [
{
"Id": 4,
"Name": "Test 6666",
"Active": true,
"MenuItems": [
]
}
]
},
{
"Id": 96,
"Name": "Test 444",
"Description": "",
"BeveragesMenu": false,
"Active": false,
"Categories": [
{
"Id": 3,
"Name": "Test 555",
"Active": true,
"MenuItems": [
]
},
{
"Id": 5,
"Name": "Test 6666",
"Active": true,
"MenuItems": [
]
},
{
"Id": 1003,
"Name": "test 777",
"Active": true,
"MenuItems": [
]
}
]
}
]
Sorry for the long JSON code paste.
So now I am running into problems when updating certain parts of the objects within arrays withing objects etc.
So I taught that the mapping plugin might solve my problem as it creates all arrays as observable and I am assuming that is my problem.
So I tried:
viewModel = {
Menu: ko.mapping.fromJSON(ko.utils.parseJson(data)),
//Menu: ko.observableArray(ko.utils.parseJson(data)),
editMenu: function (menu) {
ko.applyBindings(menu, document.getElementById("MenuCategories"));
$("#MenuCategories").bPopup();
},
But then none of my binding are working and I can't seem to find the cause, any advice or tips on this matter would be appreciated.
Here is a Fiddle of all the things I have tried. http://jsfiddle.net/armandvdwalt/pjJc2/3/
Thanks
Try this fiddle, I switched the order of the resources. You were referencing the mapping plugin before knockout which was generating a script error about not recognizing "ko." Also, you don't need to use ko.utils.parseJson when using fromJSON, as fromJSON expects JSON as an argument.