Screening conditions are the core concept of BI construction. Most of what we call exploratory analysis and chart linkage also belong to the category of screening conditions, whose essence is that one component plays a screening role in the data query of another component.

How does the filter component work

The most common filter is the query control for the form scenario, as shown below:

A number of “output capability” components as filter components, click the query button to trigger its function component recalculation.

Note that the components with “output capability” here are not only those with input properties such as input boxes, but also all components with interactive capability, even ordinary components can undertake the ability to trigger filtering:

The filtering behavior can also be triggered by clicking on the head of a table or clicking on a column of a bar graph. As long as this level of abstraction is carried out, the linkage between components is also a filtering behavior in nature.

Equally important, filtering components can also be components with input capabilities:

When the target component is a component with filtering capability, this is the filtering linkage scenario, so the filtering linkage is also a common filtering behavior. After the target component triggers the fetch, whether to modify its filter value immediately and then trigger the subsequent filter linkage is completely determined by the business characteristics.

A component can also filter itself, such as the line chart drilldown scenario, which triggers the filter itself.

What is a filter component

Any component can be a filter component.

Perhaps the easiest to understand are components with input characteristics such as input boxes, drop-down boxes, date pickers, etc. These components are only natural for filtering components, but they do not mean that the system design is special for these components.

Expand to think about it, in fact, ordinary buttons, tables, line charts and other components with display properties also have input characteristics, such as button is clicked to trigger query, cell is clicked to query the current city data trend, line chart when a line is clicked to drill down from year to month and so on.

So there is no concept of filtering components, but any component has the ability to filter, so filtering is a capability of any component, not limited to a few components. Once designed in this way, the following can be done:

  1. The realization of input class components to display class components of the screening, in line with the basic screening requirements.
  2. It is an advanced function of linkage chart to filter display class components from display class components.
  3. Realize the filtering of input class components to input class components, which belongs to the filtering linkage function.
  4. Realize the component itself to its own screening, realize the function of drilling.

The following introduces the filter criteria design of bi-Designer.

Filter condition design

Based on the above analysis, Bi-Designer does not add a so-called filter component type to the component meta-information, but sets it as a filter capability that can be triggered by any component.

How to trigger filtering

The component calls onFilterChange to do the filtering:

import { useDesigner } from "@alife/bi-designer";

const InputFilter = (a)= > {
  const { onFilterChange } = useDesigner();

 return (  <input onChange={(event)= > () => onFilterChange(event.target.value)} />  ); }; Copy the code

However, this development method violates the design concept of low invasion. We can use component-engine deconstruction to call props. OnChange directly when the input box changes.

const InputFilter = ({ onChange }) = > {
  return <input onChange={(event)= > () => onChange(event.target.value)} />;
};
Copy the code

How does the rendering engine map onFilterChange to props. OnChange? Configure the DSL as follows:

{
  "props": {
    "onChange": {
      "type": "JSExpression".      "value": "this.onFilterChange"
 }  } } Copy the code

Filter which components are affected

A general filter component selects the target component on which it is applied, similar to the following figure:

This information is stored in the component configuration of filter components, namely componentInstance. Props, screening of the target component in componentMeta. Yuan eventConfigs component is configured in the information of the event:

import { Interfaces } from "@alife/bi-designer";

const componentMeta: Interfaces.ComponentMeta = {
  eventConfigs: ({ componentInstance }) = >
componentInstance.props.targets? .map((target) = > ({
 // Filter the number  type: "filterFetch". // Trigger the component  source: componentInstance.id,  // Function component  target: target.id,  })), }; Copy the code

As shown above, assuming that the action component is stored in the props. Targets field, we set its map to type filterFetch to represent filtering, source to trigger itself, target to target component to store target.id.

When the source component calls onFilterChange, the target component triggers a fetch and gets the filter component information and filter value applied to it in the fetch parameter.

How does the component sense filtering criteria

Component fetch is combined with filter criteria. As long as filterFetch is set, the rendering engine automatically adds filters to the callback function getFetchParam that calculates fetch parameters to represent filter component information. A component can combine its own componentInstance with filters to derive the final parameter:

Finally, the component meta-information can be written to a getFetchParam callback that automatically fetches the filter component applied to it, regardless of which configuration caused the association, as long as the filter is handled responsively.

import { Interfaces } from "@alife/bi-designer";

const componentMeta: Interfaces.ComponentMeta = {
  // assemble the fetch parameters
  getFetchParam: ({ componentInstance, filters }) = > {
 // Combine componentInstance with filters.map... Returns the fetch argument  }, }; Copy the code

Frequent fetch problems caused by association between filtering components

For complex filtering linkage scenarios, the problem of frequent fetch is encountered.

Assuming that the three-level linkage screening conditions of country, province and city simultaneously apply filterFetch to a table, the filtering conditions of the table need to contain three parameters of country, province and city at the same time, but we also set the filterFetch among the three filtering components of country, province and city as the filtering linkage. So after the country switch, province change, linkage city change, the screening value will change three times in this process, but we only want the table component fetch function to execute the last time, what to do?

As shown in the figure above, each filter condition also stores a ready state in the rendering engine data stream, indicating whether the filter condition is ready or not. As long as one of the filter conditions associated with a component is not true, the component will not trigger the fetch.

Therefore, we need to always ensure that the ready of one filter component is false during the process of filtering changes. The component will take the number only after the linkage between filters is completed and the ready of all filters is true. We can use filterReady to filter the dependent configuration:

import { Interfaces, createComponentInstancesArray } from "@alife/bi-designer";

const componentMeta: Interfaces.ComponentMeta = {
  eventConfigs: ({ componentInstance }) = >
componentInstance.props.targets? .map((target) = > ({
 // Filter ready dependencies  type: "filterReady". // Trigger the component  source: componentInstance.id,  // Function component  target: target.id,  })), }; Copy the code

When the source component triggers onFilterChange, the target component’s filter ready is set to false immediately. The ready will be reset to true only if onFilterChange is triggered after the target component finishes fetching. That’a all, the other processes have no sense.

Several filter components are aggregated into a single query control

In addition to linkage, there are also appeals to prevent frequent queries. It is hoped that multiple filtering conditions can be bound into a large filtering component, and the number can be retrieved when clicking the “query” button:

You can easily do this using filter scopes in two steps:

Filter component sets independent filter scope

import { Interfaces } from "@alife/bi-designer";

const componentMeta: Interfaces.ComponentMeta = {
  // Set filterScope if it is inside a global filter
  filterScope: ({ componentInstance }) = > ["my-custom-scope-name"].}; Copy the code

Thus, the batch of filter components is in a different filter scope than the component they are working on, so filtering does not take effect immediately, and the functionality is half achieved.

Called when the confirm button is clickedsubmitFilterScope

import { useDesigner } from '@alife/bi-designer'

const componentMeta: Interfaces.ComponentMeta = {
  const { submitFilterScope } = useDesigner()
  // Call submitFilterScope('my-custom-scope-name') when click ok
}; Copy the code

You can call submitFilterScope after clicking the query button and pass in the corresponding scope name so that the scoped filter component will immediately apply to its target component.

As for the confirmation button, the UI aggregation, you can write a custom component to do this, using ComponentLoader to aggregate the filter components together to load, the function is decoupled from the UI.

If you’re interested in how this works, take a look at this picture:

Break through the filter scope

In a real scenario, however, more complex combinations may exist, as shown in the following example:

Filter 1 also generates a filterFetch for filter 2 and the table, but the action on the table is expected to be blocked by the query button, while the action on filter 2 is expected to take effect immediately. There are two ways to resolve this example:

The simplest way to do this is to set filter 1 and filter 2 to group1 with the same scope. This is achieved naturally through scope splitting, and this is essentially an example of two filters whose UI is not together, but whose scope is the same:

If filter 2 also filters the table, then we place filter 1 and filter 2 in the same group1, which means that all queries to the table are controlled by the “query” button, but we want filter 2 to work on the table immediately:

As shown in the figure, we can only set the filter scope of filter 1 to group1, so that filter 2 and the table belong to the same filter scope, and the filtering between them will take effect immediately. We just need to solve the problem that filter 1 cannot act on filter 2 immediately. You can override the filter scope with ignoreFilterScope:

import { Interfaces } from "@alife/bi-designer";

const componentMeta: Interfaces.ComponentMeta = {
  eventConfigs: ({ componentInstance }) = >
componentInstance.props.targets? .map((target) = > ({
 // Filter the number  type: "filterFetch". // Trigger the component  source: componentInstance.id,  // Function component  target: target.id,  // Break filter scope  ignoreFilterFetch: true. })), }; Copy the code

We just need the source: filter 1 target: Set ignoreFilterFetch to true in the filterFetch configuration for filter 2, and the filterFetch ignores the filter scope so that filter 1 is immediately applied to filter 2.

conclusion

Do you have any specific screening concerns? Can we solve it with this screening design?

The discussion address is: intensive reading “BI Build – Screening conditions” · Issue #270 · dT-fe /weekly

If you’d like to participate in the discussion, pleaseClick here to, with a new theme every week, released on weekends or Mondays. Front end Intensive Reading – Helps you filter the right content.

Pay attention to the front end of intensive reading wechat public account

Copyright Notice: Freely reproduced – Non-commercial – Non-derivative – Remain signed (Creative Commons 3.0 License)