How to filter with multiple condition text input in Ag-Grid? - ag-grid

Using ag-grid-enterprise v5.4.0
create multiple textFilter input ex: [CONTAINS] filterText AND [NOT CONTAIN] filterText2 just like Excel data analysis
but both filterType2 and filterText are undefined after click [APPLY FILTER]
https://embed.plnkr.co/4nAjGKmChqJiRcqz6E2n/

I believe what you are trying to do would be best achieved with the Filter Component. This allows you full control of the filter pain, whether you are in enterprise or not. Here are the required methods for each custom filter:
function CustomFilter() {}
CustomFilter.prototype.init = function (params) {
//Put any initial functions you need here (such as setting values to null)
};
CustomFilter.prototype.getGui = function () {
//return a string of HTML or a DOM element/node
};
CustomFilter.prototype.doesFilterPass = function (params) {
//Logic for your custom Filter
//return True if the row should display, false otherwise
};
CustomFilter.prototype.isFilterActive = function () {
//logic for displaying the filter icon in the header
};
CustomFilter.prototype.getModel = function() {
//store the filter state
};
CustomFilter.prototype.setModel = function(model) {
//restore the filter state here
};
Here is an example of how to implement an "Includes x but Excludes y" filter:
function DoubleFilter() {
}
DoubleFilter.prototype.init = function (params) {
this.valueGetter = params.valueGetter;
this.filterText = null;
this.setupGui(params);
};
// not called by ag-Grid, just for us to help setup
DoubleFilter.prototype.setupGui = function (params) {
this.gui = document.createElement('div');
this.gui.innerHTML =
'<div style="padding: 4px; width: 200px;">' +
'<div style="font-weight: bold;">Custom Athlete Filter</div>' +
'Include: <div><input style="margin: 4px 0px 4px 0px;" type="text" id="includesText" placeholder="Includes..."/></div>' +
'Exclude: <div><input style="margin: 4px 0px 4px 0px;" type="text" id="excludesText" placeholder="Excludes..."/></div>' +
'</div>';
// add listeners to both text fields
this.eIncludesText = this.gui.querySelector('#includesText');
this.eIncludesText.addEventListener("changed", listener);
this.eIncludesText.addEventListener("paste", listener);
this.eIncludesText.addEventListener("input", listener);
// IE doesn't fire changed for special keys (eg delete, backspace), so need to
// listen for this further ones
this.eIncludesText.addEventListener("keydown", listener);
this.eIncludesText.addEventListener("keyup", listener);
this.eExcludesText = this.gui.querySelector('#excludesText');
this.eExcludesText.addEventListener("changed", listener2);
this.eExcludesText.addEventListener("paste", listener2);
this.eExcludesText.addEventListener("input", listener2);
// IE doesn't fire changed for special keys (eg delete, backspace), so need to
// listen for this further ones
this.eExcludesText.addEventListener("keydown", listener2);
this.eExcludesText.addEventListener("keyup", listener2);
var that = this;
function listener(event) {
that.includesText = event.target.value;
params.filterChangedCallback();
}
function listener2(event) {
that.excludesText = event.target.value;
params.filterChangedCallback();
}
};
DoubleFilter.prototype.getGui = function () {
return this.gui;
};
DoubleFilter.prototype.doesFilterPass = function (params) {
var passed = true;
var valueGetter = this.valueGetter;
var include = this.includesText;
var exclude = this.excludesText;
var value = valueGetter(params).toString().toLowerCase();
return value.indexOf(include) >= 0 && (value.indexOf(exclude) < 0 || exclude == '') ;
};
DoubleFilter.prototype.isFilterActive = function () {
return (this.includesText !== null && this.includesText !== undefined && this.includesText !== '')
|| (this.excludesText !== null && this.excludesText !== undefined && this.excludesText !== '');
};
DoubleFilter.prototype.getModel = function() {
var model = {
includes: this.includesText.value,
excludes: this.excludesText.value
};
return model;
};
DoubleFilter.prototype.setModel = function(model) {
this.eIncludesText.value = model.includes;
this.eExcludesText.value = model.excludes;
};
Here is a modified plunker. I placed the filter just on the Athlete column, but the DoubleFilter can be applied to any column once it is created.
EDIT:
I realized you were asking for a rather generic double filter in your question with "Includes and Excludes" as an example. Here is a plunker that has a more generic double filter.

ag-Grid now supports this behavior, by default, in version 18.0.0.
https://www.ag-grid.com/ag-grid-changelog/?fixVersion=18.0.0

Related

Limit size of entered data in tinyMCE 5

I use tinyMCE 5 in my web site to enter data stored in a database. Therefore I need to limit the entered size, including format information, to the size of the data field. How can I prohibit the user to enter more then the allowed number of bytes, say 2000?
Best of all if I could add some information like "42/2000" on the status bar.
We had a similar requirement in our project (difference: the output should be <entered_chars>/<chars_left> instead of <entered_chars>/<max_chars>), and it ended up being a custom plugin, based on the wordcount plugin. There is some hacks in there, which could make it fail whenever tinyMCE changes, since there is no API for the statusbar in version 5 at this point of time.
But maybe you will still find it useful:
(function () {
'use strict';
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
var maxChars = function (editor) {
return editor.getParam('number_max_chars', 3600);
};
var applyMaxChars = function (editor) {
return editor.getParam('restrict_to_max_chars', true);
};
var Settings = {
maxChars: maxChars,
applyMaxChars: applyMaxChars
};
var global$1 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
var getText = function (node, schema) {
var blockElements = schema.getBlockElements();
var shortEndedElements = schema.getShortEndedElements();
var isNewline = function (node) {
return blockElements[node.nodeName] || shortEndedElements[node.nodeName];
};
var textBlocks = [];
var txt = '';
var treeWalker = new global$1(node, node);
while (node = treeWalker.next()) {
if (node.nodeType === 3) {
txt += node.data;
} else if (isNewline(node) && txt.length) {
textBlocks.push(txt);
txt = '';
}
}
if (txt.length) {
textBlocks.push(txt);
}
return textBlocks;
};
var strLen = function (str) {
return str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length;
};
var countCharacters = function (node, schema) {
var text = getText(node, schema).join('');
return strLen(text);
};
var createBodyCounter = function (editor, count) {
return function () {
return count(editor.getBody(), editor.schema);
};
};
var createMaxCount = function (editor) {
return function () {
return Settings.maxChars(editor);
}
}
var createRestrictToMaxCount = function (editor) {
return function () {
return Settings.applyMaxChars(editor);
}
}
var get = function (editor) {
return {
getCount: createBodyCounter(editor, countCharacters),
getMaxCount: createMaxCount(editor),
getRestrictToMaxCount: createRestrictToMaxCount(editor)
};
};
var global$2 = tinymce.util.Tools.resolve('tinymce.util.Delay');
function isAllowedKeycode(event) {
// allow arrow keys, backspace and delete
const key = event.keyCode;
return key === 37 || key === 38 || key === 39 || key === 40 || key === 8
|| key === 46;
}
var updateCount = function (editor, api) {
editor.getContainer().getElementsByClassName(
'tox-statusbar__text-container')[0].textContent = String(
api.getCount()) + " / " + String(
Settings.maxChars(editor) - api.getCount());
};
var setup = function (editor, api, delay) {
var debouncedUpdate = global$2.debounce(function () {
return updateCount(editor, api);
}, delay);
editor.on('init', function () {
updateCount(editor, api);
global$2.setEditorTimeout(editor, function () {
editor.on('SetContent BeforeAddUndo Undo Redo keyup', debouncedUpdate);
editor.on('SetContent BeforeAddUndo Undo Redo keydown', function (e) {
if (!isAllowedKeycode(e) && Settings.applyMaxChars(editor) &&
api.getCount() >= Settings.maxChars(editor)) {
e.preventDefault();
e.stopPropagation();
}
});
}, 0);
});
};
function Plugin(delay) {
if (delay === void 0) {
delay = 300;
}
global.add('charactercount', function (editor) {
var api = get(editor);
setup(editor, api, delay);
return api;
});
}
Plugin();
}());
Currently I'm working on a preprocessor for the paste plugin, so that the max_length effects also pasted text. That's why you see the charactercount API in the code.

Leaflet error when zoom after close popup

I created a map and added markers on it in salesforce lightning component ,
issue is:
click on marker >> click on map (this close the popup) >> zoom in and out >> this error displayed for each zoom step:
Uncaught TypeError: Cannot read property '_latLngToNewLayerPoint' of
null throws at /resource/1498411629000/leaflet/leaflet.js:9:10763
this is the code in component javascript helper:
({
drawMap: function(component,map){
var mapElement = component.find("map").getElement();
map = L.map(mapElement).setView([35.232, 40.5656], 12);
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
{
attribution: 'Tiles © Esri'
}).addTo(map);
var markers = L.marker([35.232, 40.5656]).addTo(map);
component.set("v.map", map);
component.set("v.markers", markers);
},
putMarkers: function(component) {
var map = component.get('v.map');
var markers = component.get('v.markers');
var projects = component.get('v.projects');
var projectsAction = component.get("c.getProjectsList");
var markerArray = [];
var symbol;
var symbol1 = 'symbolURl';
var symbol2 = 'symbolURl';
projectsAction.setCallback(this, function(response) {
var state = response.getState();
if( state == "SUCCESS"){
projects = response.getReturnValue();
if (markers) {
markers.remove();
}
// Add Markers
if (map && projects && projects.length> 0) {
for (var i=0; i<projects.length; i++) {
var project = projects[i];
if (project.Latitude && project.Longitude) {
var latLng = [project.Latitude, project.Longitude];
var currentStatus = project.status;
if(currentStatus=="status1")
symbol = symbol1;
else if(currentStatus=="status2")
symbol = symbol2;
var popupTemplate = '<b style="color:black; font-size:11px;">Project Name:</b><span style="float:right;margin-right:10px;color:#800000;font-size:11px;width:110px;text-align:right; white-space:normal;" > '+project.name;
var icon = new L.DivIcon({
className:'',
html: '<img style="width:34px;height:37px;" src="'+symbol+'"/>'+
'<span style="font-weight:bold;font-size:9pt;">text</span>'
});
var marker = L.marker(latLng, {project: project,icon:icon,title:project.name}).bindPopup(popupTemplate,{minWidth:200});
markerArray.push(marker);
}
}
L.layerGroup(markerArray).addTo(map);
component.set("v.map", map);
}}
else if(state == "ERROR"){
console.log('Error in calling server side action');
}
});
$A.enqueueAction(projectsAction);
}
and in javascript controller:
jsLoaded: function(component, event, helper) {
var map = component.get("v.map");
if (!map) {
helper.drawMap(component,map);
helper.putMarkers(component);
}
},
projectsChangeHandler: function(component, event, helper) {
helper.putMarkers(component);
}
where is the problem? please help
this is a new bug for Vue3,I've solved it:
change Proxy'map to toRaw(map),like this:
L.geoJSON(geoJson, {
style: {
color: '#ff0000',
fillColor: "#ff000000",
weight: 1
},
onEachFeature: myonEachFeature,
}).addTo(toRaw(map))
I solved it by editing the leaflet.js file:
search for the word ( _latLngToNewLayerPoint )
and wrap the code with the condition:
if(this._map)
so if _map is null, skip
I don't know why this bug is only presented in salesforce
In my case it was related to the framework variable wrappers (vue.js).
When I use observable variable like this:
data() {
return {
map: null,
};
},
mounted() {
// here it's required to use a global variable
// or a variable without framework handlers
// map = L.map(...);
this.map = L.map('map', { preferCanvas: true }).setView([51.505, -0.09], 13);
}
I get error:
Popup.js:231 Uncaught TypeError: Cannot read property '_latLngToNewLayerPoint' of null
at NewClass._animateZoom (Popup.js:231)
at NewClass.fire (Events.js:190)
at NewClass._animateZoom (Map.js:1689)
at NewClass.<anonymous> (Map.js:1667)
The solution is just to define the map variable outside of framework, in other words use a global variable for leaflet structures.

how to disable background-color input fields

I am using a form validation with javascript.
When submitting, the background-color of those input fields which are not valid changes to red color. When filling up this field and typing into another input field, the red background-color of the former field should go away. This is at the moment not the case. It only disappears when submitting again. How can I make this possible that the bg color changes back to normal when typing into another field?
// Return true if the input value is not empty
function isNotEmpty(inputId, errorMsg) {
var inputElement = document.getElementById(inputId);
var errorElement = document.getElementById(inputId + "Error");
var inputValue = inputElement.value.trim();
var isValid = (inputValue.length !== 0); // boolean
showMessage(isValid, inputElement, errorMsg, errorElement);
return isValid;
}
/* If "isValid" is false, print the errorMsg; else, reset to normal display.
* The errorMsg shall be displayed on errorElement if it exists;
* otherwise via an alert().
*/
function showMessage(isValid, inputElement, errorMsg, errorElement) {
if (!isValid) {
// Put up error message on errorElement or via alert()
if (errorElement !== null) {
errorElement.innerHTML = errorMsg;
} else {
alert(errorMsg);
}
// Change "class" of inputElement, so that CSS displays differently
if (inputElement !== null) {
inputElement.className = "error";
inputElement.focus();
}
} else {
// Reset to normal display
if (errorElement !== null) {
errorElement.innerHTML = "";
}
if (inputElement !== null) {
inputElement.className = "";
}
}
}
The form:
<td>Name<span class="red">*</span></td>
<td><input type="text" id="name" name="firstname"/></td>
<p id="nameError" class="red"> </p>
The submit:
<input type="submit" value="SEND" id="submit"/>
Css:
input.error { /* for the error input text fields */
background-color: #fbc0c0;
}
Update:
I tried this but it seems not to work:
function checkFilled() {
var inputVal = document.querySelectorAll("#offerteFirstname, #offerteLastname, #offertePhone, #offertePoster, #offerteStreet").value;
if (inputVal == "") {
document.querySelectorAll("#offerteFirstname, #offerteLastname, #offertePhone, #offertePoster, #offerteStreet").style.backgroundColor = "red";
}
else{
document.querySelectorAll("#offerteFirstname, #offerteLastname, #offertePhone, #offertePoster, #offerteStreet").style.backgroundColor = "white";
}
}
checkFilled();
here example of it
<input type="text" id="subEmail" onchange="checkFilled();"/>
and now you can JavaScript on input
function checkFilled() {
var inputVal = document.getElementById("subEmail").value;
if (inputVal == "") {
document.getElementById("subEmail").style.backgroundColor = "white";
}
else{
document.getElementById("subEmail").style.backgroundColor = "red";
}
}
checkFilled();
here working demo
Try
$(formElement).on('keyup','input.error', function(){
if(<values changed>){
$(this).removeClass('error');
}
});

Start search after 2 character typed in Chosen drop down

In Chosen drop down plugin, a search is started after 2 characters are typed in chosen drop down.
I need the search to not start until after inputing at least two characters in the search box.
Can any one suggest how to do this?
I did a small change to start to search after the third character, is not the best option but works, in the chosen JS in the AbstractChosen.prototype.winnow_results function after the line searchText = this.get_search_text(); add the following code: if (searchText != "" && searchText.length < 3) return;. Remember to change the < 3 by your own size.
Hope this help you
See part of the code below:
AbstractChosen.prototype.winnow_results = function() {
var escapedSearchText, option, regex, regexAnchor, results, results_group, searchText, startpos, text, zregex, _i, _len, _ref;
this.no_results_clear();
results = 0;
searchText = this.get_search_text();
if (searchText != "" && searchText.length < 3) return;
escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
I know this post it's old but i had to face this problem just now and wanted to share my result. I wrapped everything in a function extendedSearch and set it as a callback while chosen is emitting the chosen:showing_dropdown event.
My problem was that i need the search to show the results after the 2nd character typed in search and must filter out certain strings from the results.
Bellow you'll find a demo which shows results on the 3rd character typed in, and will keep visible only those results that end with letter "E".
$(function() {
/**
* By default the results are hidden when clicking a dropdown.
* {
* toggleResults: false, // a function(searchValue) that returns a boolean
* filterResults: false, // a function(dropDownValue, selectValue) that returns a boolean
* }
* #param options
*/
const extendedSearch = (options = {}) => {
const defaultOptions = {
toggleResults: false,
filterResults: false,
};
options = { ...{},
...defaultOptions,
...options
};
/**
* Main element
*/
return (evt, params) => {
let originalElement = $(evt.currentTarget);
let searchInput = params.chosen.search_field;
const customSearch = (options = {}) => {
let defaultOptions = {
originalElement: null,
searchInput: null
};
options = { ...{},
...defaultOptions,
...options
};
if (!(options.originalElement instanceof jQuery) || !options.originalElement) {
throw new Error('Custom Search: originalElement is invalid.');
}
if (!(options.searchInput instanceof jQuery) || !options.searchInput) {
throw new Error('Custom Search: searchInput is invalid.');
}
let res = options.searchInput
.parent()
.next('.chosen-results');
res.hide();
if (typeof options.toggleResults !== 'function') {
options.toggleResults = (value) => true;
}
if (options.filterResults && typeof options.filterResults !== 'function') {
options.filterResults = (shownText = '', selectValue = '') => true;
}
/**
* Search Input Element
*/
return (e) => {
let elem = $(e.currentTarget);
let value = elem.val() || '';
if (value.length && options.toggleResults(value) === true) {
res.show();
if (options.filterResults) {
let children = res.children();
let active = 0;
$.each(children, (idx, item) => {
let elItem = $(item);
let elemIdx = elItem.attr('data-option-array-index');
let shownText = elItem.text();
let selectValue = options.originalElement.find('option:eq(' + elemIdx + ')').attr('value') || '';
if (options.filterResults(shownText, selectValue) === true) {
active++;
elItem.show();
} else {
active--;
elItem.hide();
}
});
if (active >= 0) {
res.show();
} else {
res.hide();
}
}
} else {
res.hide();
}
};
};
options = {
...{},
...options,
...{
originalElement,
searchInput
}
};
let searchInstance = customSearch(options);
searchInput
.off('keyup', searchInstance)
.on('keyup', searchInstance)
}
};
/** This is the final code */
const inputValidator = (value) => {
console.log('input', value);
return $.trim(value).length > 2;
};
const textResultsValidator = (dropDownValue, selectValue) => {
if ($.trim(dropDownValue).substr(-1, 1) === 'E') {
console.log('results shown', dropDownValue, '|', selectValue);
return true;
}
return false;
};
$(".chosen-select")
.chosen()
.on('chosen:showing_dropdown', extendedSearch({
toggleResults: inputValidator,
filterResults: textResultsValidator
}));
});
#import url("https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.min.css")
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.8.7/chosen.jquery.min.js"></script>
<select class="chosen-select">
<option value="">Pick something</option>
<option value="APPLE">APPLE</option>
<option value="APPLE JUICE">APPLE JUICE</option>
<option value="BANANA">BANANA</option>
<option value="ANANAS">ANANAS</option>
<option value="ORANGE">ORANGE</option>
<option value="ORANGES">ORANGES</option>
<option value="STRAWBERRY">STRAYBERRY</option>
</select>

Select rows with checkbox in SlickGrid

I need to make a column where you can select grid rows with input:checkbox. Grids like ones used by Yahoo, Google etc have something like that.
I made something but I have some problems and I think that is not a good aproach also.
It's posible to have checkboxes in rows and click on them directly, not like in example4-model ?
My idea was :
< div id="inlineFilterPanel" class="slick-header-column" style="padding: 3px 0; color:black;">
<input type="checkbox" name="selectAll" id="selectAll" value="true" / >
< input type="text" id="txtSearch2" value="Desktops" />
</div>
d["check"] = '< INPUT type=checkbox value='true' name='selectedRows[]' id='sel_id_<?php echo $i; ?>' class='editor-checkbox' hideFocus />';
grid.onSelectedRowsChanged = function() {
selectedRowIds = [];
$('#myGrid' + ' :checkbox').attr('checked', '');
var rows = grid.getSelectedRows();
for (var i = 0, l = rows.length; i < l; i++) {
var item = dataView.rows[rows[i]];
if (item) {
selectedRowIds.push(item.id);
$('#sel_' + item.id).attr('checked', 'checked');
}
}
};
function selectAllRows(bool) {
var rows = [];
selectedRowIds = [];
for (var i = 0; i < dataView.rows.length; i++) {
rows.push(i);
if (bool) {
selectedRowIds.push(dataView.rows[i].id);
$('#sel_' + dataView.rows[i].id).attr('checked', 'checked');
} else {
rows = [];
$('#sel_' + dataView.rows[i].id).attr('checked', '');
}
}
grid.setSelectedRows(rows);
}
grid.onKeyDown = function(e) {
// select all rows on ctrl-a
if (e.which != 65 || !e.ctrlKey)
return false;
selectAllRows(true);
return true;
};
$("#selectAll").click(function(e) {
Slick.GlobalEditorLock.cancelCurrentEdit();
if ($('#selectAll').attr('checked'))
selectAllRows(true);
else
selectAllRows(false);
return true;
});
Thanks!
I've added a sample implementation of a checkbox select column to http://mleibman.github.com/SlickGrid/examples/example-checkbox-row-select.html
This is part of the upcoming 2.0 release.