npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue-renderless-expression-builder

v0.6.50

Published

![codecov](https://github.com/hubagaspar91/vue-renderless-expression-builder/blob/master/coverage.svg)

Downloads

5,144

Readme

Renderless Expression Builder for Vue.js

codecov

This is something I needed for a project, where I had to build an expression (or query) builder, with a layout drastically different from the standard query builder interface (such as the jQuery QueryBuilder), but with very similar functionality. This library is a layout-agnostic implementaiton, meaning, that it aims to serve as a core for flexibly creating query builder components with different complexities and specifications.

Demo implementation (with Bootstrap Vue)
Demo code

What it does:

  • Creates and manages the nested structure (nodes and node groups)
  • Let's you define a set of available fields for filtering (First Name, Created At, etc.), field types (date, text, number, bool, etc.), and operators (equals, in, etc.).
  • Let's you define a max depth for the query generated by the builder
  • Based on the above parameters, creates the actual conditions for the individual expression nodes

What it does not do:

  • Provide any layout implementation

Installation

npm install vue-renderless-expression-builder

Available named exports are:

  • Core - the underlying model objects
  • Components - the actual renderless components
  • Conditions - the default conditions and conditionFactory class, used internally by the renderless components

Components

ExpressionNodeRenderless

Props: | Prop | Type | Description | |---|---|---| | node | Core.ExpressionNode | node represented by the component |

Available scopedSlots: | Slot | Type | Description | |---|---|---| | node | Core.ExpressionNode | node object, represented by the component | | index | number | index of the current node, in its parent's children array | | updateCondition | Function(fieldName: string, operatorName: string, value: any) | calls ConditionFactory.create with the params and updates the condition of the current node | | deleteSelf | Function | deletes the current node | | conditionFactory | ConditionFactory | condition factory instance |

ExpressionNodeGroupRenderless

Props: | Prop | Type | Description | |---|---|---| | node | Core.ExpressionNodeGroup | node group represented by the component |

Available scopedSlots: | Slot | Type | Description | |---|---|---| | node | Core.ExpressionNodeGroup | node group object, represented by the component | | index | number | index of the current node, in its parent's children array | | deleteSelf | Function | deletes the current node | | conditionFactory | ConditionFactory | condition factory instance | | toggleConnectionType | Function | toggles group's connection type between 'and' and 'or' | | addNode | Function(condition: Object?) | adds a node at the end of the childrens array of the group | | addGroup | Function | adds a node group at the end of the childrens array of the group | | insert | Function(node: Core.ExpressionNode(Group), index: number) | inserts a node or node group at a given index |

ExpressionBuilderRenderless

Props: | Prop | Type | Description | |---|---|---| | value | Core.ExpressionBuilder? | ExpressionBuilder instance to be represented by the component | | fields | ConditionFactoryField[] (below) | list of user defined fields to be used in the builder | | operators | ConditionFactoryOperator[] (below) | a list of operators available in the builder - default: Conditions.returnDefaultOperators | | fieldTypes | ConditionFactoryFieldTypeDefinition[] (below) | a list of field types available in the builder - default: Conditions.returnDefaultFieldTypes | | eventHub | Vue? | eventHub, that the builder and nodes use to communicate |

Provided keys (provide/inject API): | Key | Type | Description | |---|---|---| | $__qb_condition_factory__ | ConditionFactory | ConditionFactory, that's injected to all ExpressionNodeRenderless and ExpressionNodeGroupRenderless components, but you can inject in the implementations as well | | $__qb__event_hub__ | Vue | eventHub, that the builder and nodes use to communicate |

Core

Core.ExpressionNode

| Member | Type | Description | |---|---|---| | parentNode | Core.ExpressionNodeGroup | The node group containing this node | | toJSON | Function | Returns a json representation of the current condition object | | fromJSON (static) | Function(json: Object) |Creates a new Core.ExpressionNode instance from a json representation | | condition | Object | Can be any object, however, the conditionFactory creates the condition objects with a specific schema - see below |

Condition object schema:

{
  fieldName: "string",  // name of the field (fisrtName, dateOfBirth, etc.)
  operatorName: "string",  // name of the operator (equals, in, etc.)
  fieldTypeName: "string"  // name of the field type (text, date, etc.)
  value: any  // condition value
}

Core.ExpressionNodeGroup

Core.ExpressionNodeGroup constructor params:
| Param | Type | Description | |---|---|---| | opts | Object | default: {maxDepth: 0, currentDepth: 0, children: [], connectionType: "and"} | | parentNode | Core.ExpressionNodeGroup | The node group containing this node |

Core.ExpressionNodeGroup members:
| Member | Type | Description | |---|---|---| | parentNode | Core.ExpressionNodeGroup | The node group containing this node | | maxDepth | number | the max depth of the nested structure, defaults to 0 (meaning infinite depth)| | currentDepth | number | the depth of the current node group in the current nested strucure | | toJSON | Function | Recursively creates a json representation of the current node group and its children | | fromJSON (static) | Function(json: Object) | reates a new Core.ExpressionNodeGroup object from a json representation | | flatten | Function | returns a 1 depth array of arrays, flattening the nested condition, where the elements of each array are conditions of ExpressionNodes and are connected by AND and the inner arrays are connected by OR, so the result can be used for client-side filtering like this: list.filter(elem => flattened.map(group => group.every(condition => validateCondition(condition, elem))).some(groupIsTrue => groupIsTrue)) |

Core.ExpressionBuilder

Core.ExpressionBuilder constructor params | Param | Type | Description | |---|---|---| | root | Core.ExpressionNodeGroup? or Object? (json representation) | if it's undefined, a new Core.ExpressionNodeGroup is created | | errorHandler | Function(key: string, msg: string) | called when an error occurs (like reaching max depth), can be used for notifying the user of errors |

Core.ExpressionBuilder members: | Member | Type | Description | |---|---|---| | root | Core.ExpressionNodeGroup | the root node group of the current expression/query | | context | Core.ExpressionNodeGroup | the current context, for which insertion and deletion method calls apply | | insert | Function(node: Core.ExpressionNode(Group), index: number) | Inserts a node or node group at a given index in the children array of the context node group. If a node group was inserted, sets it as the new context. Returns the context, for fluent editing | | set | Function(node: Core.ExpressionNode(Group), index: number) | Sets a node at a for a given index in the children array of the context node group. If a node group was inserted, sets it as the new context, Returns the context, for fluent editing | | delete | Function(index: number) | Deletes a node from a given index in the children array of the context node group. Returns the context, for fluent editing | | add | Function(node: Core.ExpressionNode(Group)) | Inserts a node or node group at the end of the children array of the context node group. If a node group was inserted, sets it as the new context, Returns the context, for fluent editing | | contextUp | Function | sets the parentNode of the current context as the new context | | contextToRoot | Function | sets the root as the new context | | contextTo | Function(path: number[]) | sets a node group by a path from the root, as the new context ie. the first child of the root node can be set as context by calling contextTo([0]) | | flatten | Function | Calls the faltten method of the root Core.ExprssionNodeGroup | | toJSON | Function | Calls the toJSON method of the root Core.ExpressionNodeGroup |

Conditions

ConditionFactory

ConditionFactory constructor opts

interface ConditionFactoryOpts {
  fields: ConditionFactoryField[];
  operators?: ConditionFactoryOperator[];
  fieldTypes?: ConditionFactoryFieldTypeDefinition[];
}

| Param | Type | Description | |---|---|---| | fields | ConditionFactoryField[] (below) | list of user defined fields to be used with the conditionFactory | | operators | ConditionFactoryOperator[] (below) | a list of operators available in the conditionFactory - default: returnDefaultOperators | | fieldTypes | ConditionFactoryFieldTypeDefinition[] (below) | a list of field types available in the conditionFactory - default: returnDefaultFieldTypes |

ConditionFactory members: | Member | Type | Description | |---|---|---| | fields | ConditionFactoryField[] | fields provided in the constructor | | operators | ConditionFactoryOperator[] | operators provided in the constructor | | fieldTypes | ConditionFactoryFieldTypeDefinition[] | fieldTypes provided in the constructor | | create | Function(fieldName: string, operatorName: string, value: any) | returns: ConditionFactoryCondition (below) | | createAndUpdate | Function(node: Core.ExpressionNode, fieldName: string, operatorName: string, value: any) | returns: void - updates the condition key of the current node with a newly created condition |

interface ConditionFactoryField {
  type: string;
  name: string;
  label: string;
  operators?: string[];
  choices?: any[];  // used, if select-type type is used
}

interface ConditionFactoryOperator {
  name: string;
  label: string;  // operator display name
}

interface ConditionFactoryFieldTypeDefinition {
  name: string;
  availableOperators: string[];  // operators available by default for the field type
  label: string;  // display name for the field type
}

export interface ConditionFactoryCondition {
  fieldName: string;
  fieldTypeName: string;
  operatorName: string;
  value: any;
}

Defaults

defaultFieldTypes

const defaultFieldTypes = {
  TEXT: "text",
  DATE: "date",
  NUMBER: "number",
  BOOLEAN: "boolean",
  CHOICE: "radio",
  MULTIPLE_CHOICE: "multipleChoice",
  SELECT: "select"
};

defaultOperators

const defaultOperators = {
  EQUALS: "equals",
  NOT_EQUALS: "notEquals",
  GREATER_THAN: "graterThan",
  LOWER_THAN: "lowerThan",
  IN: "in",
  NOT_IN: "notIn",
  STARTS_WITH: "startsWith",
  NOT_STARTS_WITH: "notStartsWith",
  ENDS_WITH: "endsWith",
  NOT_ENDS_WITH: "notEndsWith",
  IS_EMPTY: "isEmpty",
  NOT_IS_EMPTY: "notIsEmpty",
  IS_NULL: "isNull",
  NOT_IS_NULL: "notIsNull",
  IS_ONE_OF: "isOneOf",
  NOT_IS_ONE_OF: "notIsOneOf"
};

returnDefaultFieldTypes

[{
	"name": "text",
	"label": "text",
	"availableOperators": ["equals", "notEquals", "isEmpty", "notIsEmpty", "endsWith", "notEndsWith", "startsWith", "notStartsWith", "isNull", "notIsNull", "in", "notIn"]
},
{
	"name": "date",
	"label": "date",
	"availableOperators": ["equals", "notEquals", "isNull", "notIsNull", "graterThan", "lowerThan"]
},
{
	"name": "number",
	"label": "number",
	"availableOperators": ["equals", "notEquals", "isNull", "notIsNull", "graterThan", "lowerThan"]
},
{
	"name": "boolean",
	"label": "boolean",
	"availableOperators": ["equals"]
},
{
	"name": "radio",
	"label": "radio",
	"availableOperators": ["equals", "notEquals"]
},
{
	"name": "multipleChoice",
	"label": "multiple choice",
	"availableOperators": ["in", "notIn"]
},
{
	"name": "select",
	"label": "select",
	"availableOperators": ["equals", "notEquals"]
}]

returnDefaultOperators

[{
    "name": "equals",
    "label": "equals"
}, {
    "name": "notEquals",
    "label": "not equals"
}, {
    "name": "graterThan",
    "label": "greater than"
}, {
    "name": "lowerThan",
    "label": "lower than"
}, {
    "name": "in",
    "label": "in"
}, {
    "name": "notIn",
    "label": "not in"
}, {
    "name": "startsWith",
    "label": "starts with"
}, {
    "name": "notStartsWith",
    "label": "doesn't start with"
}, {
    "name": "endsWith",
    "label": "ends with"
}, {
    "name": "notEndsWith",
    "label": "doesn't end with"
}, {
    "name": "isEmpty",
    "label": "is empty"
}, {
    "name": "notIsEmpty",
    "label": "is not empty"
}, {
    "name": "isNull",
    "label": "is null"
}, {
    "name": "notIsNull",
    "label": "is not null"
}]