Is it possible to customise a Floating Filter to handle Regular Expressions - javascript

I would like to be able to use the free text input of the Floating Filter as a regular expression parser. Meaning that, for example, if in my Floating Filter I Have a value "Greg*", the filtered data should contain all values starting with "Greg".
Expected Usage Example
I want to be able to implement my own regular expressions rules to it. I understand I can do it in the filter button on the right but I want to do it on the free text input visible, without having the user clicking on a button.

I solved my question after looking more closely to the ag-grid documentation, thing that I should have done from the start 🙂
Since Floating Filters are actually showing their parent Filter state, one should then implement his own custom Filter and Floating Filter together. So I implemented two classes CustomTextFilter and CustomTextFloatingFilter, implementing respectively IFilterComp and IFloatingFilterComp from the ag-grid library.
export class CustomTextFilter implements IFilterComp{
// IFilterComp method implementations here [...]
}
export class CustomTextFloatingFilter implements IFilterComp{
// IFilterComp method implementations here [...]
}
Within CustomTextFilter class, the most important is to implement the doesFilterPass method, where we can implement our regexp logic. Below is an example with a rule that allows us to search for multiple strings separated by a comma:
doesFilterPass(params: IDoesFilterPassParams): boolean {
var passed = false;
var valueGetter = this.valueGetter;
var filterText = this.eFilterText.value
if (this.isFilterActive()) {
var value = valueGetter(params);
passed = filterText.toLowerCase().split(",").some((word: any) => {
return word !== null && word !== undefined && word.trim() !== ''
&& value.toString().toLowerCase().trim().indexOf(word) >= 0;
});
}
return passed;
}
Then in CustomFloatingFilter we make sure that we that we listen to the input event so we can transfer the value from the floating filter to the parent filter, and then refresh the grid:
init(params: IFloatingFilterParams): void {
// Some gui code here [...]
this.eFilterInput = this.gui.querySelector('input');
this.eFilterText = this.gui.querySelector('#floatingFilterText');
var that = this;
this.eFilterText.addEventListener("input", (event: any) => {
that.filterText = event.target.value;
params.parentFilterInstance((instance: any) => {
instance.setModel({ value: that.filterText });
instance.gridApi.onFilterChanged();
});
});
}
Hope this will help!

Related

How can I pass innerHTML to an onClick function (Typescript)

I'm trying to pass the Square element's innerHTML to the onClick function. I have also tried to pass in just i but it always is equal to 100. Is there a way to either pass i when it's equal to the same value that goes into the Square or is there a way to pass the innerHTML to the function. Currently, this code generates the error:
[TS: 2532]this is possibly undefined
I'm making a grid of 100 squares, each one is a button, and each one should have it's own ID/number from 1-100 to identify them.
This is what I have currently: Grid of 100 squares arranged in 10x10 formation
export const Square = (props:any) =>{
i += 1;
if(i > 100)
{
i = 1;
}
return(
<DefaultButton styles={factionMapButton} onClick={()=>onSquareClick(this.innerHTML,props.onClick)}>{i}</DefaultButton>
);
}
const onSquareClick = (number:any,isOpen:any) => {
console.log(number);
const panelContent = document.getElementById("panelContent");
if(panelContent !== null)
{
panelContent.innerHTML = number;
}
isOpen();
}
You have quite a few problems.
You should do your best to avoid any in TypeScript, especially not liberally - that defeats the whole purpose of type-checking. If you're trying to fix type problems, you should start by also typing everything properly.
Arrow functions do not have their this altered by the calling context. If there's no enclosing full-fledged function, the this in an arrow function will be the global object or undefined, both of which are useless to you. Either use a function to capture the this, or, even better, use the click event's currentTarget to get a reference to the clicked button.
The .innerHTML of an element returns a string, not an element. If it contains a string that can be coerced to a number, explicitly coerce it to a number instead. (If the HTML content is only the string that can be coerced to the number, you should use .textContent instead - only use .innerHTML when deliberately setting or retrieving HTML markup, not plain text)
A better approach would be to pass down the i to onSquareClick instead of using DOM manipulation - using the closure is much easier
let i = 1;
export const Square = ({ onClick }: { onClick: () => void }) => {
i += 1;
if (i > 100) {
i = 1;
}
return (
<DefaultButton styles={factionMapButton} onClick={(e) => { onSquareClick(i, onClick); }}>{i}</DefaultButton>
);
};
const onSquareClick = (number: number, isOpen: () => void) => {
const panelContent = document.getElementById('panelContent');
if (panelContent !== null) {
panelContent.innerHTML = String(number);
}
isOpen();
};
If you're using React, you should not be using vanilla DOM manipulation like panelContent.innerHTML = number; - instead, set React state that the view uses to determine what should exist in that element. Something like
// Parent component:
const [panelContentText, setPanelContentText] = useState('');
// expand as needed for the other components in your app...
return <div id="panelContent">{panelContentText}</div>
<Square setPanelContentText={setPanelContentText} /* and other props */ />
// ...
// Then call the setPanelContentText prop in onSquareClick
const onSquareClick = (number: number, isOpen: () => void, setPanelContentText: React.Dispatch<React.SetStateAction<string>>) => {
setPanelContentText(String(number));
isOpen();
};
I'd recommend looking into an introductory React tutorial, it looks like you might benefit from learning the process within React's ecosystem, rather than trying to mishmash your understanding of vanilla JS's DOM with React.

Filter an Array of Objects from an Array in TypeScript

I built a custom component that filters an array of objects. The filter uses buttons, sets from active to non-active and allows more than one option on/off at the same time.
StackBlitz of my attempt - https://stackblitz.com/edit/timeline-angular-7-ut6fxu
In my demo you will see 3 buttons/options of north, south and east. By clicking on one you make it active and the result should include or exclude a matching "location" either north, south and east.
I have created my methods and structure to do the filtering, I'm struggling with the final piece of logic.
So far I have created a method to create an array of filtered locations depending on what the user clicks from the 3 buttons.
Next this passes to my "filter array" that gets the logic that should compare this filtered array against the original to bring back the array of results that are still remaining.
Its not quite working and not sure why - I originally got this piece of functionality working by using a pipe, but fore reasons do not want to go in that direction.
//the action
toggle(location) {
let indexLocation = this.filteredLocations.indexOf(location);
if (indexLocation >= 0) {
this.filteredLocations = this.filteredLocations.filter(
i => i !== location
);
} else {
this.filteredLocations.push({ location });
}
this.filterTimeLine();
}
// the filter
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
//the logic
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
https://stackblitz.com/edit/timeline-angular-7-ut6fxu
Sorry for my expression but u have a disaster in your code. jajaja!. maybe u lost that what u need but the logic in your functions in so wrong. comparing string with objects. filter a array that filter the same array inside... soo u need make a few changes.
One:
this.filteredLocations.push({location});
Your are pushing object. u need push only the string.
this.filteredLocations.push(location);
Two:
filterTimeLine() {
this.filteredTimeline = this.timeLine.filter(x =>
this.contactMethodFilter(x)
);
}
in this function you filter the timeLine array. and inside of contactMethodFilter you call filter method to timeLine again....
See a functional solution:
https://stackblitz.com/edit/timeline-angular-7-rg7k3j
private contactMethodFilter(entry) {
const myArrayFiltered = this.timeLine.filter(el => {
return this.filteredLocations.some(f => {
return f.location === el.location;
});
});
}
This function is not returning any value and is passed to the .filter
Consider returning a boolean based on your logic. Currently the filter gets undefined(falsy) and everything would be filtered out

Draft.js - CompositeDecorator: Is there a way to pass information from the strategy to the component?

Lets say my strategy calculates some numbered label. How can I pass this (e.g. via props) to the decorator component.
I know there is a props property in CompositeDecorator but how can I access it from the strategy function?
I'm a bit new to DraftJs, but based on my understanding:
Strategies should be used to identify the range of text that need to be decorated. The rendering of that decoration (which presumably includes calculating what the label should be) should be handled in the component itself, rather than the strategy.
You should be able to access the ContentState via the props object in your component, and calculate the label from that. The constructor of your component could be a good place for executing the logic for calculating the label. This also means that you might have to use a class definition for your decorator components as opposed to a pure function as shown in the examples on the draftjs website.
You can also circumvent the issue by reading the values from the text with regex. The following example is done with #draft-js-plugins:
// How the section is detected.
const strategy = (contentBlock, callback) => {
const text = contentBlock.getText();
const start = text.indexOf('<_mytag ');
const endTag = '/>';
const end = text.indexOf(endTag);
if (start !== -1 && end !== -1) {
callback(start, end + endTag.length);
}
};
// What is rendered for the detected section.
const component = ({ decoratedText }) => {
if (decoratedText) {
const label = decoratedText.match(/label="([a-zA-Z0-9/\s]*?)"/);
if (
label &&
typeof label[1] === 'string'
) {
return <div>{label[1]}</div>;
}
return null;
}
};
export const MyTagPlugin = {
decorators: [
{
strategy,
component,
},
],
};

Javascript polymorphism without OOP classes

In JS or OOP language the polymorhpism is created by making different types.
For example:
class Field {...}
class DropdownField extends Field {
getValue() {
//implementation ....
}
}
Imagine I have library forms.js with some methods:
class Forms {
getFieldsValues() {
let values = [];
for (let f of this.fields) {
values.push(f.getValue());
}
return values;
}
}
This gets all field values. Notice the library doesnt care what field it is.
This way developer A created the library and developer B can make new fields: AutocompleterField.
He can add methods in AutocompleterField withouth changing the library code (Forms.js) .
If I use functional programming method in JS, how can I achieve this?
If I dont have methods in object i can use case statements but this violates the principle. Similar to this:
if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..
If developer B add new type he should change the library code.
So is there any good way to solve the issue in javascript without using object oriented programming.
I know Js isnt exactly OOP nor FP but anyway.
Thanks
JavaScript being a multi-purpose language, you can of course solve it in different ways. When switching to functional programming, the answer is really simple: Use functions! The problem with your example is this: It is so stripped down, you can do exactly the same it does with just 3 lines:
// getValue :: DOMNode -> String
const getValue = field => field.value;
// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);
readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']
The critical thing is: How is Field::getValue() implemented, what does it return? Or more precisely: How does DropdownField::getValue() differ from AutocompleteField::getValue() and for example NumberField::getValue()? Do all of them just return the value? Do they return a pair of name and value? Do they even need to be different?
The question is therefor, do your Field classes and their inheriting classes differ because of the way their getValue() methods work or do they rather differ because of other functionality they have? For example, the "autocomplete" functionality of a textfield isn't (or shouldn't be) tied to the way the value is taken from it.
In case you really need to read the values differently, you can implement a function which takes a map/dictionary/object/POJO of {fieldtype: readerFunction} pairs:
/* Library code */
// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;
// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;
// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;
// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);
/* Code the library consumer writes */
const readMyForm = readFieldsBy({
'input[type="text"]': getTextInputValue,
'select': getDropdownValue,
'textarea': getTextareaValue
});
readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']
Note: I intentionally didn't mention things like the IO monad here, because it would make stuff more complicated, but you might want to look it up.
In JS or OOP language the polymorhpism is created by making different types.
Yes. Or rather, by implementing the same type interface in different objects.
How can I use Javascript polymorphism without OOP classes
You seem to confuse classes with types here. You don't need JS class syntax to create objects at all.
You can just have
const autocompleteField = {
getValue() {
…
}
};
const dropdownField = {
getValue() {
…
}
};
and use the two in your Forms instance.
Depends on what you mean by "polymorphism". There's the so-called ad-hoc polymorphism which type classes in Haskell, Scala, or PureScript provide -- and this kind of dispatch is usually implemented by passing witness objects along as additional function arguments, which then will know how to perform the polymorphic functionality.
For example, the following PureScript code (from the docs), which provides a show function for some types:
class Show a where
show :: a -> String
instance showString :: Show String where
show s = s
instance showBoolean :: Show Boolean where
show true = "true"
show false = "false"
instance showArray :: (Show a) => Show (Array a) where
show xs = "[" <> joinWith ", " (map show xs) <> "]"
example = show [true, false]
It gets compiled to the following JS (which I shortened):
var Show = function (show) {
this.show = show;
};
var show = function (dict) {
return dict.show;
};
var showString = new Show(function (s) {
return s;
});
var showBoolean = new Show(function (v) {
if (v) {
return "true";
};
if (!v) {
return "false";
};
throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});
var showArray = function (dictShow) {
return new Show(function (xs) {
return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
});
};
var example = show(showArray(showBoolean))([ true, false ]);
There's absolutely no magic here, just some additional arguments. And at the "top", where you actually know concrete types, you have to pass in the matching concrete witness objects.
In your case, you would pass around something like a HasValue witness for different forms.
You could use a the factory pattern to ensure you follow the open close principle.
This principle says "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
class FieldValueProviderFactory {
getFieldValue(field) {
return this.providers.find(p => p.type === field.type).provider(field);
}
registerProvider(type, provider) {
if(!this.providers) {
this.providers = [];
}
this.providers.push({type:type, provider:provider});
}
}
var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);
class FieldCollection {
getFieldsValues() {
this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];
let values = [];
for (let field of this.fields) {
values.push(provider.getFieldValue(field));
}
return values;
}
}
Now when you want to register new field types you can register a provider for them in the factory and don't have to modify your field code.
new Field().getFieldsValues();

Kendo UI Grid - Add/Remove filters dynamically

I need to Create a Kendo ui grid. Since this has many filters, I need to have 4 regular filters and rest should be able to add dynamically according to users choice. Can someone provide assistance on this?
In order to filter by text box you can hook up a keyUp event in order to retrieve the value. You can then add this as a filter to the existing filter object.
$('#NameOfInput').keyup(function () {
var val = $('#NameOfInput').val();
var grid = $("#yourGrid").data("kendoGrid");
var filter = grid.dataSource.filter();
filter.filters.push({
field: "NameOfFieldYouWishToFilter",
operator: "eq",
value: val,
FilterName: "UniqueIdentifierForFilter"
});
grid.dataSource.filter(filter);
});
Using a drop down box, you can achieve the desired functionality by using the onChange event, get the value using $('#yourDropDown').val();.
The FilterName is optional incase you require additional logic to add/remove filters. i.e. you can use this to determine whether the filter already exists in the array and if so you can use splice to remove it.
EDIT
Using FilterName you can search to see if a filter already exists and remove it:
var filterIndex = filter.filters.map((e: any) => { return e.FilterName }).indexOf("UniqueIdentifierForFilter");
if (filterIndex > -1)
{
filter.filters.splice(filterIndex, 1);
}
For #lakshan, while this is largely correct, you will get an error if there are no filters at first. I found this answer when I encountered the undefined filter error. My full solution for adding a filter, either to an undefined filter set, or along with an existing one:
var grid = $("#ActivityGrid").data("kendoGrid");
var dataSource = grid.dataSource;
var gridFilter = dataSource.filter();
var upcomingFilter = {
field: "ActivityDate",
operator: "gte",
value: new Date(),
FilterName: "UpcomingOnly"
};
if ($("#UpcomingOnlyCheckbox")[0].checked) {
if (gridFilter == undefined) {
dataSource.filter(upcomingFilter);
}
else {
gridFilter.filters.push(upcomingFilter);
dataSource.filter(gridFilter);
}
}

Categories