
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
react-crux
Advanced tools
Simple react based library to create components for basic and complex CRUD operations
CRUX is a simple to use client side library to create UI for CRUD operations on server side entities. Its best suited to create admin dashboards which typically have complex relationships between entities.
It is not a replacement for React or Redux or Bootstrap but rather a higher level abstraction that lets you create simple UI for CRUD operations by just writing a JSON config. CRUX reads this config and creates a React Component that you can add to your app.
Since its a client side library, it is completely agnostic to server side tech stack.
If you use CRUX, you would not have to write HTML/JSX/TSX code. Essentially it converts a config in JSON to a UI that creates a table with all objects and a model to create new objects or modify existing objects in a user friendly manner.
1- Install react-crux and it's dependencies
yarn add react-crux react-bootstrap bootstrap-sass
2- Add react crux css and bootstrap to your app.scss
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
$bootstrap-sass-asset-helper: false;
@import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";
@import "~@curefit/react-crux/scss/crux.scss";
3- Write the config and pass it to the factory method to create the component (config guide explained later)
import * as React from "react"
import { CruxComponentCreator } from "@curefit/react-crux"
const constants = {
modelName: "serviceAccess",
title: "Service Access",
creationTitle: "Service Access",
createModal: true,
editModal: true,
largeEdit: true,
stateRoot: "none",
fields: [
{
title: "Service",
field: "serviceName",
representative: true,
},
{
title: "Users",
field: "users",
type: "iterable",
iterabletype: {
title: "User",
inlineEdit: true
}
}
]
}
const ServiceAccessComponent = CruxComponentCreator.create<ServiceAccess, ServiceAccessProps>(constants)
export default ServiceAccessComponent
4- Create the CRUX reducer by using the factory method and it to your redux app
import { applyMiddleware, combineReducers, createStore } from "redux"
import { createLogger } from "redux-logger"
import thunk from "redux-thunk"
import { CruxReducerFactory } from "@curefit/react-crux"
...
const appReducer = combineReducers({crux: CruxReducerFactory({}), user: UserReducer})
...
const store = createStore(
appReducer,
applyMiddleware(thunk, createLogger())
)
5- Use the exported component in your react app like you do with any component.
"dependencies": {
"autobind-decorator": "^2.1.0", // Because binding manually is so 2017
"lodash": "^4.17.10", // Used heavily for all list/object manipulations
"moment": "^2.22.2", // Used in date picker
"moment-timezone": "^0.5.23", // Used in date picker with timezone
"react": "^16.4.2", // Duh
"react-bootstrap": "^0.32.3", // Its pretty cool
"react-bootstrap-typeahead": "^3.2.2", // For typeahead component
"react-datepicker": "^1.6.0", // For datepicker
"react-datetime": "^2.16.3", // For datepicker with timezone
"react-timezone": "^2.3.0", // Used in datepicker with timezone
"react-dropzone": "^5.0.1", // For file upload component
"react-redux": "^5.0.7", // Duh
"react-select": "^2.3.0", // For Multi Select component
"superagent": "^3.8.3", // For upload request
"query-string": "^6.2.0" // For Query Params formation
"react-color": "^2.17.0" // For Color Pallete Component Addition
"reactcss": "^1.2.3" // Dynamic Css For Color Pallete
}
This helps us to filter records from the table. The fields with filterParameter=true will be displayed inside filter modal.
const constants = {
modelName: "serviceAccess",
title: "Service Access",
creationTitle: "Service Access",
createModal: true,
editModal: true,
filterModal: true,
largeEdit: true,
stateRoot: "none",
fields: [
{
title: "Service",
field: "serviceName",
representative: true,
filterParameter: true
},
{
title: "Users",
field: "users",
type: "iterable",
iterabletype: {
title: "User",
inlineEdit: true
}
}
]
}
const ServiceAccessComponent = CruxComponentCreator.create<ServiceAccess, ServiceAccessProps>(constants)
export {ServiceAccessComponent}
Most common use case after text fields is to have a field whose value is restricted to a set of values. This set might be small and static and so might be hardcoded as enums or constants. This set might be big and dynamic so its values might come from another api or collection in the database. For CRUX schema it does not matter.
For fields with type: "select", another field foreign is mandatory. This field tells CRUX where to get the options for select from. Three fields are mandatory in foreign
client | server specifies whether to filter at client or server, defaults to client{
title: "Media Type",
field: "mediaType",
display: true,
editable: true,
type: "select",
foreign: {
modelName: "mediaTypes",
key: "typeId", // typeId is what will be stored while storing mediaType for the object
title: "title" // title is what will be used to show in the dropdown
}
}
// Above example assumes that /model/mediaTypes returns a response like
[
{
typeId: "IMAGE",
title: "Image"
},
{
typeId: "VIDEO",
title: "Video"
}
]
This helps us to select multiple values in List. multiClear will allow us to clear multi values
{
title: "Media Type",
field: "mediaType",
display: true,
editable: true,
type: "multiselect",
multiClear: true
foreign: {
modelName: "mediaTypes",
key: "typeId", // typeId is what will be stored while storing mediaType for the object
title: "title" // title is what will be used to show in the dropdown
}
}
This helps us to select color in Color Pallete.
{
title: "Text Color",
field: "textColor",
display: true,
editable: true,
type: "colorpallete"
}
This helps us to select multiple values in List.
{
title: "Media Type",
field: "mediaType",
display: true,
editable: true,
type: "searcheableselect",
foreign: {
modelName: "mediaTypes",
key: "typeId", // typeId is what will be stored while storing mediaType for the object
title: "title" // title is what will be used to show in the dropdown
}
}
Select Field with Customized Filter Option. Modal Values can be filtered in that Custom Filter Function based on the requirement
{
editable: true,
title: "Attribute Name",
type: "select",
field: "id",
foreign: {
modelName: "cohortEventMeta",
key: "id",
title: "name",
transform: customFilter
}
}
// Example
function customFilter(dataSource: any, currentModel: any, additionalModels: any, parentModel: any ) {
const cohortEventMetas = additionalModels[dataSource]
let attributes
// Filter Logic
return attributes
}
Typeahead Field with Customized Filter Option. Modal Values can be filtered in that Custom Filter Function based on the requirement
{
editable: true,
title: "Attribute Name",
type: "typeahead",
field: "id",
foreign: {
modelName: "cohortEventMeta",
key: "id",
title: "name",
transform: customFilter
}
}
// Example
function customFilter(dataSource: any, currentModel: any, additionalModels: any, parentModel: any ) {
const cohortEventMetas = additionalModels[dataSource]
let attributes
// Filter Logic
return attributes
}
Dynamic Typeahead Field which will query based on user typings. It is not supported to show these values in table. As it leads more Db hit
{
editable: true,
title: "Attribute Name",
type: "dynamicTypeahead",
field: "id",
foreign: {
modelName: "cohortEventMeta",
key: "id",
title: "name",
dynamicPayloadFn: ({parentModel}) => parentModel.data
}
}
Iterable Dynamic Typeahead Field which will query based on user typings. It is not supported to show these values in table. As it leads more Db hit
bulkKey in foreign object => will contain all the ids to fetch initially
{
title: "Nicknames",
field: "nicknames",
type: "iterable",
editable: true,
iterabletype: {
title: "Nickname",
type: "dynamicTypeahead",
foreign: {
bulkKey: "ids",
modelName: "names",
key: "id",
title: "name"
}
}
}
Iterable Dynamic MultiSelect Field which will query based on user typings. It is not supported to show these values in table. As it leads more Db hit
bulkKey in foreign object => will contain all the ids to fetch initially
{
editable: true,
title: "Attribute Name",
type: "dynamicMultiselect",
field: "id",
foreign: {
bulkKey: "ids",
modelName: "cohortEventMeta",
key: "id",
title: "name"
}
}
Whenever one of fields is a list of other objects/strings, set type: "iterable". To define the underlying type use the field iterabletype. It follows the same schema as field and supports all features mentioned above Example
{
title: "Nicknames",
field: "nicknames",
type: "iterable",
editable: true,
iterabletype: {
type: "text",
title: "Name"
}
}
Whenever one of fields is a list of other objects/strings, set type: "iterable". To define the underlying type use the field iterabletype. It follows the same schema as field and supports all features mentioned above . Iterable Field will have re-order button Example
{
title: "Nicknames",
field: "nicknames",
type: "iterable",
editable: true,
additionalButtons: {
reorder: true
},
iterabletype: {
type: "text",
title: "Name"
}
}
Whenever one of fields is a list of other objects/strings, set type: "iterable". To define the underlying type use the field iterabletype. It follows the same schema as field and supports all features mentioned above . Iterable Field will have Add At Index Button Example
{
title: "Nicknames",
field: "nicknames",
type: "iterable",
editable: true,
additionalButtons: {
addAtIndex: true
},
iterabletype: {
type: "text",
title: "Name"
}
}
Whenever one of fields is a list of other objects/strings, set type: "iterable". To define the underlying type use the field iterabletype. It follows the same schema as field and supports all features mentioned above Iterable Field will also have re-order buttons Example
{
title: "Nicknames",
field: "nicknames",
type: "iterable",
editable: true,
additionalButtons: {
customButton: true,
customButtonAction: customButtonAction,
},
iterabletype: {
type: "text",
title: "Name"
}
}
function customIterableButtonAction(data: any) {
// Custom Button Action will be captured here
}
If the field is itself an object containing more fields, its type should be "nested". A field with "nested" type should have another mandatory field called fields. This is a list of all fields inside the nested object and each field follows the same schema as above. Example
{
"modelName": "employees",
"title": "Employees with list of free-form Tags",
"creationTitle": "Employee",
"editModal": true,
"fields": [
{
"title": "Name",
"field": "name",
"editable": true,
"representative": true,
"display": true
},
{
"title": "Address",
"editable": true,
"display": true,
"field": "address",
"type": "nested",
"fields": [
{
"title": "Address Type",
"field": "type",
"display": true,
"editable": true,
"type": "select",
"foreign": {
"modelName": "addressTypes",
"key": "typeId",
"title": "displayName"
}
},
{
"title": "Address Line 1",
"field": "addressLine1",
"display": true,
"editable": true
},
{
"title": "Address Type",
"field": "addressLine2",
"display": true,
"editable": true
},
{
"title": "City",
"field": "city",
"display": true,
"editable": true
},
{
"title": "ZipCode",
"field": "zipcode",
"display": true,
"editable": true,
"type": "tinyinput"
}
]
}
],
"createModal": true
}
This is to support default values for our components. In each config, we can represent a defaultValueFn key. Custom Function will be called, when the component does not have value.
{
"title": "Text",
"editable": true,
"display": true,
"type": "text",
"defaultValueFn": () => "Initial Default Value"
},
{
"title": "Checkbox",
"editable": true,
"display": true,
"type": "checkbox",
"defaultValueFn": () => true
},
{
"title": "Number",
"editable": true,
"display": true,
"type": "number",
"defaultValueFn": () => 123
}
This is to support boolean fields. If the field is not present in the object, the edit modal shows it "unchecked" and saving does not set it. Otherwise that field is set as true or false (based on state). Example
{
"title": "Is Part Time ?",
"editable": true,
"display": true,
"field": "isPartTime",
"type": "checkbox"
}
Datepicker is a cool widget to show fields which are dates and to modify them. We use react-datepicker to render dates. The underlying api needs to return the value which moment understands. If moment().format() returns a properly formatted date, CRUX will be able to handle it. Otherwise it will lead to errors. Example
{
"title": "Date Of Joining",
"editable": true,
"display": true,
"field": "joiningDate",
"type": "datepicker"
}
Datepicker is a cool widget to show fields which are dates and to modify them. We use react-datepicker to render dates. The underlying api needs to return the value which moment understands. If moment().format() returns a properly formatted date, CRUX will be able to handle it. Otherwise it will lead to errors. Datepicker will also time select option Example
{
"title": "Date Of Joining",
"editable": true,
"display": true,
"field": "joiningDate",
"type": "datepicker",
"showTimeSelect": true
}
Datepicker is a cool widget to show fields which are dates and to modify them. We use react-datetime to render dates. The underlying api needs to return the value which moment understands. If moment().format() returns a properly formatted date, CRUX will be able to handle it. Otherwise it will lead to errors. Datepicker will also time select option
Here data will be stored as an object with ( date and timezone ) ex: joiningDate = { date: 2019-03-06 05:30:00.000Z, timezone: "Asia/Kolkata" }
{
"title": "Date Of Joining",
"editable": true,
"display": true,
"field": "joiningDate",
"type": "datetimezonepicker"
}
This is to support fields that require a image/file upload. When type is imageUpload, another field called contentType becomes mandatory. Finally for upload a http post call to /content/:contentType/upload/ is made. If width and height are specified in the schema, they are also sent as part of form data with the file.
{
editable: true,
width: 100,
height: 100,
title: "App Image",
field: "image",
contentType: "image",
type: "imageUpload"
},
This is to support that require a Custom Modal with Custom Button in Table.
{
"modelName": "employees",
"title": "Employees with list of free-form Tags",
"creationTitle": "Employee",
"editModal": true,
"fields": [
{
"title": "Name",
"field": "name",
"editable": true,
"representative": true,
"display": true
},
{
"title": "Address",
"field": "address",
"editable": true,
"display": true
},
],
"createModal": true,
"customModal": true,
"customModalIcon": "glyphicon glyphicon-trash",
"customModalComponent": CustomModalComponent
}
// ModalComponent.tsx
class CustomModalComponent extends React.Component<any, any> {
render() {
return (<ModalComponent
constants={CustomModalConstants}
showModal={true}
closeModal={this.props.closeModal}
modalType={"CUSTOM"}
successButtonLabel={"CLONE"}
item={model}
createOrModify={this.props.customModalSuccess}
additionalModels={[]}
/>)
}
}
This is to support Custom Components with our edit/create Modal.
{
"modelName": "employees",
"title": "Employees with list of free-form Tags",
"creationTitle": "Employee",
"editModal": true,
"fields": [
{
"title": "Name",
"field": "name",
"editable": true,
"representative": true,
"display": true
},
{
title: "Address",
type: "custom",
customComponent: customComponentFn, // Deprecated
customViewComponent: CustomViewComponent
}
],
"createModal": true
}
// @Deprecated
function customComponentFn(currentModal: model, additionalModels: any, parentModel: any, addtionalProps: any, modal: any) {
class CustomComponent extends React.Component<{}, {}> {
render() {
return <p>{model.address}</p>
}
}
return CustomComponent
}
class CustomViewComponent extends React.Component<{}, {}> {
render() {
return <p>{this.props.currentModal.address}</p>
}
}
This is to support Custom Components with our edit/create Modal. This will allow us to create our own support with our own state.
{
"modelName": "employees",
"title": "Employees with list of free-form Tags",
"creationTitle": "Employee",
"editModal": true,
"fields": [
{
"title": "Name",
"field": "name",
"editable": true,
"representative": true,
"display": true
},
{
title: "Address",
field: "address",
type: "customedit",
editable: true,
customEditComponent: CustomEditComponent
}
],
"createModal": true
}
// Props for this CustomEditComponent is currentModal, additionalModels, parentModel, addtionalProps, field, handleChange
export class CustomEditComponent extends React.Component<any, any> {
constructor(props: any) {
super(props)
this.state = {
address = "India"
}
}
handleChange = () => {
this.setState({ address = "America" })
// React Crux change event props to be called here. To reflect changes in crux (redux)
this.props.handleChange(this.props.field, value)
}
render() {
return <div>
<p>{this.state.address}</p>
<button onClick={this.handleChange}>Change Address</button>
</div>
}
}
For a lot of values (e.g. enums, constants), typically its not desired to fetch them from the API server via http call. To support this, CRUX supports injecting of default models through the CRUX reducer. e.g.
// DefaultModels.tsx
const DefaultModels = {
mediaTypes: [
{
typeId: "IMAGE",
title: "Image"
},
{
typeId: "VIDEO",
title: "Video"
}
],
addressTypes: [
{
typeId: "HOME",
title: "Home"
},
{
typeId: "OFFICE",
title: "Office"
},
{
typeId: "OTHERS",
title: "Other"
},
]
}
// index.tsx (Main React App)
import { DefaultModels } from "./DefaultModels"
const appReducer = combineReducers({crux: CruxReducerFactory(DefaultModels), ...})
const store = createStore(
appReducer,
applyMiddleware(thunk, createLogger())
)
Filtering and Ordering (Client Side) will be performed. Search Bar will shown above the Table. Based on search value, data will be loaded in Table
{
"modelName": "employees",
"title": "Employees with list of free-form Tags",
"creationTitle": "Employee",
"editModal": true,
"enableSearch": true
"fields": [
{
"title": "Name",
"field": "name",
"editable": true,
"representative": true,
"display": true
},
{
"title": "Address",
"field": "address",
"editable": true,
"display": true
}
],
"createModal": true
}
The Field will have conditionalField and ConditionalValue as their Attributes. The Field will be rendered only when conditionalValue matched with Conditional Field Value
{
"modelName": "employees",
"title": "Employees with list of free-form Tags",
"creationTitle": "Employee",
"editModal": true,
"fields": [
{
"title": "Name",
"field": "name",
"editable": true,
"representative": true,
"display": true
},
{
"title": "Address",
"editable": true,
"display": true,
"field": "address",
"type": "nested",
"fields": [
{
"title": "Address Type",
"field": "type",
"display": true,
"editable": true,
"type": "select",
"foreign": {
"modelName": "addressTypes",
"key": "typeId",
"title": "displayName"
}
},
{
"title": "Address Line",
"field": "home",
"display": true,
"editable": true,
"conditionalField": "type",
"conditionalValue": "residential",
},
{
"title": "Address Line",
"field": "office",
"display": true,
"editable": true,
"conditionalField": "type",
"conditionalValue": "office",
},
{
"title": "City",
"field": "city",
"display": true,
"editable": true
},
{
"title": "ZipCode",
"field": "zipcode",
"display": true,
"editable": true,
"type": "tinyinput"
}
]
}
],
"createModal": true
}
Config Based Styles Can be Applied to Components.
{
title: "",
field: "units",
display: true,
editable: true,
type: "select",
style: {
hideLabel: true
},
foreign: {
modelName: "timeUnits",
key: "typeId",
title: "title"
}
}
{
title: "",
field: "units",
display: true,
editable: true,
type: "iterable",
style: {
border: "none"
},
iterabletype: {
type: "nested",
title: "Scales",
fields: [
{
title: "Smiley Type",
editable: true,
display: false,
field: "smileyType",
type: "text"
}
]
}
}
{
title: "",
field: "units",
display: true,
editable: true,
type: "nested",
style: {
forceIndent: true
},
fields: [
{
title: "Smiley Type",
editable: true,
display: false,
field: "smileyType",
type: "text"
}
]
}
We can give queryParams in config. It will append queryparams with every fetch call.
import * as React from "react"
import { CruxComponentCreator } from "@curefit/react-crux"
const constants = {
modelName: "serviceAccess",
title: "Service Access",
creationTitle: "Service Access",
createModal: true,
editModal: true,
largeEdit: true,
stateRoot: "none",
fields: [
{
title: "Service",
field: "serviceName",
representative: true,
},
{
title: "Users",
field: "users",
type: "iterable",
iterabletype: {
title: "User",
inlineEdit: true
}
}
]
}
const ServiceAccessComponent = CruxComponentCreator.create<ServiceAccess, ServiceAccessProps>(constants)
class ComponentWithQueryParams extends React.Component<{}, {}> {
render() {
return (<ServiceAccessComponent {...this.props} options={{ queryParams: { data: "1", title: "check" }}}/>)
}
}
export {ComponentWithQueryParams}
We can pass props to crux component as below.
import * as React from "react"
import { CruxComponentCreator } from "@curefit/react-crux"
const constants = {
modelName: "serviceAccess",
title: "Service Access",
creationTitle: "Service Access",
createModal: true,
editModal: true,
largeEdit: true,
stateRoot: "none",
fields: [
{
title: "Service",
field: "serviceName",
representative: true,
},
{
title: "Users",
field: "users",
type: "iterable",
iterabletype: {
title: "User",
inlineEdit: true
}
}
]
}
const ServiceAccessComponent = CruxComponentCreator.create<ServiceAccess, ServiceAccessProps>(constants)
class ComponentWithProps extends React.Component<{}, {}> {
render() {
return (<ServiceAccessComponent {...this.props} options={{ queryParams: { data: "1", title: "check" }, additionalProps: {...this.props}} />)
}
}
export {ComponentWithProps}
A crux component when mounted does the following in order
All the live examples can be found at https://curefit.github.io/react-crux-examples The code for the examples can be found at https://github.com/curefit/react-crux-examples
Some example snippets have been copied below for convenience.
Lets say we want to show a table of employees with 3 fields (name, employeeId, emailId) with a functionality to create, modify and delete employees
const schema = {
modelName: "employees", // http call to /model/employees
title: "Employees", // Title for the table
creationTitle: "Employee", // Create button will show "+ New Employee"
createModal: true, // Enable creation of new employees through modal
editModal: true,
largeEdit: true,
stateRoot: "none",
fields: [ // We have 3 fields - name, age, emailAddress
{
title: "Name",
field: "name",
representative: true,
display: true, // We want to display it in table
editable: true, // We want to be able to edit it
},
{
title: "Age",
field: "age",
display: false, // We _dont_ want to display it in table
editable: true, // We want to be able to edit it
},
{
title: "Email Address",
field: "emailAddress",
display: true, // We want to display it in table
editable: true, // We want to be able to edit it
}
]
}
const Employees = CruxComponentCreator.create<Employee, EmployeeWodProps>(schema)
export { Employees }
Lets say we want to show a table of employees with 3 fields (name, employeeId, emailId) with a functionality to create, modify, save as new and delete employees
const schema = {
modelName: "employees", // http call to /model/employees
title: "Employees", // Title for the table
creationTitle: "Employee", // Create button will show "+ New Employee"
createModal: true, // Enable creation of new employees through modal
editModal: true,
saveAsNew: true,
largeEdit: true,
stateRoot: "none",
fields: [ // We have 3 fields - name, age, emailAddress
{
title: "Name",
field: "name",
representative: true,
display: true, // We want to display it in table
editable: true, // We want to be able to edit it
},
{
title: "Age",
field: "age",
display: false, // We _dont_ want to display it in table
editable: true, // We want to be able to edit it
},
{
title: "Email Address",
field: "emailAddress",
display: true, // We want to display it in table
editable: true, // We want to be able to edit it
}
]
}
const Employees = CruxComponentCreator.create<Employee, EmployeeWodProps>(schema)
export { Employees }
Lets say we want to show a table of employees with 4 fields (name, joiningDate, isPartTime, nicknames) with a functionality to create, modify and delete employees. Readonly attribute is applicable for all the components.
const schema = {
modelName: "employees", // http call to /model/employees
title: "Employees", // Title for the table
creationTitle: "Employee", // Create button will show "+ New Employee"
createModal: true, // Enable creation of new employees through modal
editModal: true,
largeEdit: true,
stateRoot: "none",
fields: [ // We have 4 fields - name, joiningDate, isPartTime, nicknames
{
title: "Name",
field: "name",
representative: true,
display: true, // We want to display it in table
readonly: true // We want to be able to disable it
},
{
"title": "Date Of Joining",
"readonly": true,
"display": true,
"field": "joiningDate",
"type": "datepicker"
},
{
"title": "Is Part Time ?",
"readonly": true,
"display": true,
"field": "isPartTime",
"type": "checkbox"
},
{
title: "Nicknames",
field: "nicknames",
type: "iterable",
readonly: true,
iterabletype: {
type: "text",
title: "Name"
}
}
]
}
const Employees = CruxComponentCreator.create<Employee, EmployeeWodProps>(schema)
export { Employees }
Readonly attribute is applicable for all the components. Readonly value can either be boolean or Function. For Example. You can write your own function to dynamic set readonly for a particular input. Return of that function has to be boolean
{
title: "Name",
field: "name",
representative: true,
readonly: setReadOnly
}
function setReadOnly(data): boolean {
if (data.status === true) {
return true
}
return false
}
Since components created using CRUX are actual react components, you can render as many CRUX components on a page or inside another component. Since they are all backed by same Redux store, they also share all the models and dont make redundant http requests if some of the underlying models are same.
const employeeSchema = {
modelName: "employees",
...
}
const projectSchema = {
modelName: "projects",
...
}
const Employees = CruxComponentCreator.create<Employee, EmployeeProps>(employeeSchema)
const Projects = CruxComponentCreator.create<Project, ProjectProps>(projectSchema)
export class EmployeeContainer extends React.Component<{}, {}> {
render() {
return <div>
<Employees />
<Projects />
</div>
}
}
One very common pattern is to have a field which is a list of objects. In CRUX terminology that translates to iterable of nested. The example below shows how to model it. The example if for products which typically have list of media attached to them. Each media object can either be a image or video and have a url.
{
title: "Media",
field: "media",
display: false,
editable: true,
type: "iterable",
iterabletype: {
type: "nested",
title: "Media",
fields: [
{
title: "Media Type",
field: "type",
display: true,
editable: true,
type: "select",
foreign: {
modelName: "mediaTypes",
key: "typeId",
title: "title"
}
},
{
title: "Media Url",
field: "url",
display: true,
editable: true,
}
]
}
}
Addition of Support for Collapsed Nested Iterable Component. Each Iterable component can be expanded based on necessity.
{
title: "Media",
field: "media",
display: false,
editable: true,
type: "iterable",
iterabletype: {
type: "nested",
title: "Media",
nestedIterableCollapse: {
default: true,
title: "Banner"
},
fields: [
{
title: "Media Type",
field: "type",
display: true,
editable: true,
type: "select",
iterableRepresentative: true,
foreign: {
modelName: "mediaTypes",
key: "typeId",
title: "title"
}
},
{
title: "Media Url",
field: "url",
display: true,
editable: true,
}
]
}
}
Lets say we want to show a table of employees with 3 fields (name, age, emailAddress) with a functionality to create, modify and delete employees with server side pagination. (i.e. server call will be pointed to /filter rather than /fetch)
const schema = {
modelName: "employees", // http call to /model/employees
title: "Employees", // Title for the table
creationTitle: "Employee", // Create button will show "+ New Employee"
createModal: true, // Enable creation of new employees through modal
editModal: true,
largeEdit: true,
stateRoot: "none",
paginate: {
defaultPageSize : 10,
allowedPageSizes : [10, 50, 100, 500, 1000]
},
fields: [ // We have 3 fields - name, age, emailAddress
{
title: "Name",
field: "name",
representative: true,
display: true, // We want to display it in table
editable: true // We want to be able to edit it
},
{
title: "Age",
field: "age",
display: false, // We _dont_ want to display it in table
editable: true // We want to be able to edit it
},
{
title: "Email Address",
field: "emailAddress",
display: true, // We want to display it in table
editable: true // We want to be able to edit it
}
]
}
const Employees = CruxComponentCreator.create<Employee, EmployeeWodProps>(schema)
export { Employees }
FAQs
Simple react based library to create components for basic and complex CRUD operations
The npm package react-crux receives a total of 40 weekly downloads. As such, react-crux popularity was classified as not popular.
We found that react-crux demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.