I want to update autocomplete suggestions according to the string.
aceeditorObj.completers.push({
getCompletions: function(editor, session, pos, prefix, callback) {
obj = editor.getSession().getTokenAt(pos.row, pos.column-count);
if(obj.value === "student"){
var wordList = ["name", "age" , "surname"];
callback(null, wordList.map(function(word) {
return {
caption: word,
value: word,
meta: "static"
};
}));
}
}
});
Name, age and surname is added to the auto suggestion list. But the old ones are still there. How can I show only the new world list in the list of auto completion?
Try setting the language tools to blank after you call your completers function:
var langTools = ace.require("ace/ext/language_tools");
aceeditorObj.completers.push({
getCompletions: function(editor, session, pos, prefix, callback) {
obj = editor.getSession().getTokenAt(pos.row, pos.column-count);
if(obj.value === "student"){
var wordList = ["name", "age" , "surname"];
callback(null, wordList.map(function(word) {
return {
caption: word,
value: word,
meta: "static"
};
}));
}
}
});
langTools.setCompleters([]); // This function should clear them
Related
So I have an array which looks like this:
[
{ TransactionValues: '50.00' },
{ TransactionValues: '-77.43' },
{ TransactionValues: '-20.23' },
{ TransactionValues: '200.23' }
]
I am trying to find a way to target the monetary value and create a variable based on the sum of these. When I try to target the "50.00" for example I get "Undefined" and it's still an array.
I'm not exactly sure how I can target it specifically, is it possible? Any help would be appreciated
As per the comments here is the full code (be wary I'm still learning so it's not elegant):
var fs = require('fs');
var parse = require('csv-parse');
var transactionValues = []; //Need an array to hold transactions
var currentTrans = [];
var savingsTrans = [];
//constuctor for transactions
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : transactions
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = {
"TransactionValues" : transactions
}
currentTrans.push(cTrans);
}
else {
var sTrans = {
"TransactionValues" : transactions
}
savingsTrans.push(sTrans);
}
};
//parses the csv file, loops each row and adds it to the transactionValue array
var parser = parse({columns: true}, function (err, results) {
console.table(results);
for (const row of results) {
addData(row.AccountID, row.AccountType, row.InitiatorType, row.DateTime, row.TransactionValue );
logTrans(row.AccountType, row.TransactionValue);
}
console.log(transactionValues);
console.log(currentTrans);
console.log(savingsTrans);
});
fs.createReadStream(__dirname+'/testData/customer-1234567-ledger.csv').pipe(parser)
not completely following but at the end of the day you have an array like data below.
you can use filter to target the attribute you want.
you can use map to pull out just the values.
you can use reduce to sum them all up.
run the snippet below to see each step
const data = [
{ TransactionValues: '50.00', AccountType: 'CURRENT' },
{ TransactionValues: '-77.43', AccountType: null},
{ TransactionValues: '-20.23', AccountType: 'CURRENT' },
{ TransactionValues: '200.23', AccountType: null }
];
const CurrentTrans = data.filter((x) => x.AccountType === 'CURRENT');
const SavingTrans = data.filter((x) => x.AccountType !== 'CURRENT');
console.log('CurrentTrans');
console.log(CurrentTrans);
console.log('SavingTrans');
console.log(SavingTrans);
const CurrentTransValues = CurrentTrans.map((x) => parseFloat(x.TransactionValues));
const SavingTransValues = SavingTrans.map((x) => parseFloat(x.TransactionValues));
console.log('CurrentTransValues');
console.log(CurrentTransValues);
console.log('SavingTransValues');
console.log(SavingTransValues);
const TotalCurrentValues = CurrentTransValues.reduce((sum, x) => sum + x);
const TotalSavingValues = SavingTransValues.reduce((sum, x) => sum + x);
console.log('TotalCurrentValues');
console.log(TotalCurrentValues.toFixed(2));
console.log('TotalSavingValues');
console.log(TotalSavingValues.toFixed(2));
So I may have fixed it by using parseFloat in my addData and logTrans functions:
function addData (id, accountType, initiatorType, dateTime, transactions) {
var data = {
"AccountID" : id,
"AccountType" : accountType,
"InitiatorType" : initiatorType,
"DateTime" : dateTime,
"TransactionValues" : parseFloat(transactions)
}
transactionValues.push(data); //should add a new line
}
function logTrans (accountType, transactions) {
if (accountType == "CURRENT") {
var cTrans = parseFloat(transactions);
currentTrans.push(cTrans);
}
else {
var sTrans = parseFloat(transactions);
savingsTrans.push(sTrans);
}
};
Now that seems to of worked. So I can use the "Sum values of objects in array" as suggested before. Thank you everyone :)
This plunker uses a rhyming dictionary to do autocompletion.
var langTools = ace.require("ace/ext/language_tools");
var editor = ace.edit("editor");
editor.setOptions({enableBasicAutocompletion: true});
// uses http://rhymebrain.com/api.html
var rhymeCompleter = {
getCompletions: function(editor, session, pos, prefix, callback) {
if (prefix.length === 0) { callback(null, []); return }
$.getJSON(
"http://rhymebrain.com/talk?function=getRhymes&word=" + prefix,
function(wordList) {
// wordList like [{"word":"flow","freq":24,"score":300,"flags":"bc","syllables":"1"}]
callback(null, wordList.map(function(ea) {
return {name: ea.word, value: ea.word, score: ea.score, meta: "rhyme"}
}));
})
}
}
langTools.addCompleter(rhymeCompleter);
For example, type "trace" and hit ctrl-space, and only a few suggestions pop up (retrace, interlace, interface).
The rhyming dictionary actually suggested many other matches, and they were passed in to the callback, but internally Ace filtered them out because they don't contain the letters "t", "r", "a", "c", and "e". Is there a way to bypass that filter so that it suggests all of the things that were passed to the callback?
The Autocompletion is filtered by ace in the autocompleter file. But you can set insertMatch to avoid this filter,
insertMatch: function(editor, data) {
editor.completer.insertMatch({value: data.value})
});
You can update the callback function in this way:
callback(null,
wordList.map(function (word) {
return {
caption: word,
value: word,
completer: {
insertMatch: function (editor, data) {
editor.completer.insertMatch({value: data.value})
}
}
}
In my summernote hint option I have the following
hint: {
match: /\B[#!](\w*)$/,
search: function (keyword, callback) {
callback($.grep(mentions, function (i) {
user = mentionData[i];
return user.name.indexOf(keyword) === 0;
}));
},
template: function...
content: function (i) {
var user = mentionData[i];
return $('<span>#' + user.username + '</span>')[0];
}
}
I want to be able to insert the # or ! symbol (whichever one the user initiated the hint with) into the content option. Is there a way to pass the keyword to the content function? or is there a way specific to summernote to do this?
You can use array hints, example:
hint:[
{
words: ['apple', 'orange', 'watermelon', 'lemon'],
match: /\b(\w{1,})$/,
search: function (keyword, callback) {
callback($.grep(this.words, function (item) {
return item.indexOf(keyword) === 0;
}));
},
{
mentions: ['jayden', 'sam', 'alvin', 'david'],
match: /\B#(\w*)$/,
search: function (keyword, callback) {
callback($.grep(this.mentions, function (item) {
return item.indexOf(keyword) == 0;
}));
},
content: function (item) {
return '#' + item;
}
}
]
}
},
Hi I am trying to work with the store filterBy function but i am unable to make it work as I want.
I have four buttons(A B C D) and a list with all data related to buttons
When I click on the button I should filter accordingly
Note:- when I click A ,I should get records of A
when I click B , I should get records of B appended to A's list of records
So actually when I click on the buttons I should have a array of button ids and filter the the list using these id's.
filterList:function (list, index, target, record){
var categories=[];
categories.push(record.get('id'));
for(c=0;c<categories.length;c++)
{
Ext.getStore('mystore').filterBy(function(record){
var id = record.get('id');
if(id==categories[c])
{
return true;
}
});
}
}
Any help is appreciated.
What is wrong with your code
The filterBy return function should always return a boolean value. You are only returning true, and no false. Besides that you are filtering the store in a loop, and the parameter record exists twice (as a parameter in the function filterList and in the return function of the filterBy).
Example
I created a Fiddle to show you where I came up with. I know it is ExtJs instead of Sencha Touch, but you will get the idea. I will step through the MainController, where all the logic is.
I didn't clean the code so there is some duplication, but it's just a concept.
Setting up some logic
First I create a set of buttons on my view. See that I'm setting an action property.
...
tbar: [{
text: 'Filter a',
action: 'a'
}, {
text: 'Filter b',
action: 'b'
}],
...
Then I bind an onClick event to the button. In this event I use the action property as a searchValue, but it could be anything of course.
...
onClick: function(button, event, eOpts) {
var me = this,
grid = me.getMainGrid(),
store = grid.getStore(),
filters = store.getFilters(),
searchValue = button.action,
regex = RegExp(searchValue, 'i'),
filter = new Ext.util.Filter({
id: button.id,
filterFn: function(record) {
var match = false;
Ext.Object.each(record.data, function(property, value) {
match = match || regex.test(String(value));
});
return match;
}
});
if (filters.containsKey(button.id)) {
store.removeFilter(filter);
} else {
store.addFilter(filter);
}
},
...
The magic
The magic is in the filter instance. By adding new filter instances each time you filter, you can filter with several filters. If you push button a and button b they will respect each other. In that filter I use a regex to search through all data of the current model. I config an id to recognize the filter so I can remove it afterwards.
filter = new Ext.util.Filter({
id: button.id,
filterFn: function(record) {
var match = false;
Ext.Object.each(record.data, function(property, value) {
match = match || regex.test(String(value));
});
return match;
}
});
If the filter doesn't exists it will be added, otherwise it will be removed.
if (filters.containsKey(button.id)) {
store.removeFilter(filter);
} else {
store.addFilter(filter);
}
Reset filters
I also created a textfield to search through all data. Instead of adding and removing filters I just call clearFilter and add a new filter with new searchvalue.
onKeyUp: function(textField, event, eOpts) {
this.filterStore(textField.getValue());
},
filterStore: function(searchValue) {
var me = this,
grid = me.getMainGrid(),
store = grid.getStore(),
regex = RegExp(searchValue, 'i');
store.clearFilter(true);
store.filter(new Ext.util.Filter({
filterFn: function(record) {
var match = false;
Ext.Object.each(record.data, function(property, value) {
match = match || regex.test(String(value));
});
return match;
}
}));
}
The complete maincontroller
Ext.define('Filtering.controller.MainController', {
extend: 'Ext.app.Controller',
config: {
refs: {
mainGrid: 'maingrid'
}
},
init: function() {
var me = this;
me.listen({
global: {},
controller: {},
component: {
'segmentedbutton': {
toggle: 'onToggle'
},
'toolbar > button': {
click: 'onClick'
},
'textfield': {
keyup: 'onKeyUp'
}
},
store: {
'*': {
metachange: 'onMetaChange',
filterchange: 'onFilterChange'
}
},
direct: {}
});
},
onLaunch: function() {
var store = Ext.StoreManager.lookup('Users') || Ext.getStore('Users');
store.load();
},
onMetaChange: function(store, metaData) {
var grid = this.getMainGrid(),
model = store.getModel(),
// metadata
fields = metaData.fields,
columns = metaData.columns,
gridTitle = metaData.gridTitle;
model.fields = fields;
grid.setTitle(gridTitle);
grid.reconfigure(store, columns);
},
onFilterChange: function(store, filters, eOpts) {
var me = this,
grid = me.getMainGrid();
grid.getSelectionModel().select(0);
},
/**
* Used for the segmented buttons
*/
onToggle: function(container, button, pressed) {
var me = this,
grid = me.getMainGrid(),
store = grid.getStore(),
//filters = store.getFilters(),
searchValue = button.action,
regex = RegExp(searchValue, 'i'),
filter = new Ext.util.Filter({
id: button.id,
filterFn: function(record) {
var match = false;
Ext.Object.each(record.data, function(property, value) {
match = match || regex.test(String(value));
});
return match;
}
});
if (pressed) {
store.addFilter(filter);
} else {
store.removeFilter(filter);
}
},
/**
* Used for the toolbar buttons
*/
onClick: function(button, event, eOpts) {
var me = this,
grid = me.getMainGrid(),
store = grid.getStore(),
filters = store.getFilters(),
searchValue = button.action,
regex = RegExp(searchValue, 'i'),
filter = new Ext.util.Filter({
id: button.id,
filterFn: function(record) {
var match = false;
Ext.Object.each(record.data, function(property, value) {
match = match || regex.test(String(value));
});
return match;
}
});
if (filters.containsKey(button.id)) {
store.removeFilter(filter);
} else {
store.addFilter(filter);
}
},
/**
* Used for the textfield
*/
onKeyUp: function(textField, event, eOpts) {
this.filterStore(textField.getValue());
},
filterStore: function(searchValue) {
var me = this,
grid = me.getMainGrid(),
store = grid.getStore(),
regex = RegExp(searchValue, 'i');
store.clearFilter(true);
store.filter(new Ext.util.Filter({
filterFn: function(record) {
var match = false;
Ext.Object.each(record.data, function(property, value) {
match = match || regex.test(String(value));
});
return match;
}
}));
}
});
The complete view
Ext.define('Filtering.view.MainGrid', {
extend: 'Ext.grid.Panel',
xtype: 'maingrid',
tbar: [{
xtype: 'segmentedbutton',
allowMultiple: true,
items: [{
text: 'Filter a',
action: 'a'
}, {
text: 'Filter b',
action: 'b'
}]
}, {
text: 'Filter a',
action: 'a'
}, {
text: 'Filter b',
action: 'b'
}, {
xtype: 'textfield',
emptyText: 'Search',
enableKeyEvents: true
}],
//title: 'Users', // Title is set through the metadata
//store: 'Users', // we reconfigure the store in the metachange event of the store
columns: [] // columns are set through the metadata of the store (but we must set an empty array to avoid problems)
});
Assuming you have two toggle buttons and you want to filter store on toggling of those buttons,
Controller
Ext.define('namespace',{
extend:'controller...',
config:{
refs:{
btnA:'#btnA',
btnB:'#btnB',
},
controls:{
btnA:{
change:'btnChange'
},
btnB:{
change:'btnChange'
}
}
},
btnChange:function(ths,val){
var btnA = this.getBtnA().getValue(),
btnB = this.getBtnB().getValue();
Ext.getStore('mystore').filterBy(function(r){
if(btnA && btnB){
return r.get('id') === btnA.getValue() || r.get('id') === btnB.getValue();
}else if(btnA){
return r.get('id') === btnA.getValue();
}else if(btnB){
return r.get('id') === btnB.getValue();
}
return false;
});
}
});
I am using Ace Editor v.1.1.8 with ext-language_tools.
I want to achieve the following behavior with auto-complete:
User starts typing, presses Ctrl+Space and I show him the list of found captions, when he selects one of them the value is inserted.
My code:
var completions = [
{id: 'id1', 'value': 'value1'},
{id: 'id2', 'value': 'value2'}
];
var autoCompleter = {
getCompletions: function(editor, session, pos, prefix, callback) {
if (prefix.length === 0) {
callback(null, []);
return;
}
callback(
null,
completions.map(function(c) {
return {value: c.id, caption: c.value};
})
);
}
};
editor.setOptions({
enableBasicAutocompletion: [autoCompleter],
enableLiveAutocompletion: false,
enableSnippets: false
});
So what I need from the above is that user enters 'val', sees the list with 'value1' and 'value2', selects one of them and 'id1' or 'id2' is inserted into editor.
At this point:
Editor always searches by value (and i need to search by caption)
If I set 'value' = c.value, then editor will search correctly but will insert c.value while I need c.id inserted.
Here's the working code: http://plnkr.co/edit/8zAZaLpZVhocHmI4GWhd?p=preview
UPD:
Was able to achieve this behavior by adding insertMatch method to data:
completions.map(function(c) {
return {
value: c.name,
meta: c.id,
completer: {
insertMatch: function(editor, data) {
if (editor.completer.completions.filterText) {
var ranges = editor.selection.getAllRanges();
for (var i = 0, range; range = ranges[i]; i++) {
range.start.column -= editor.completer.completions.filterText.length;
editor.session.remove(range);
}
}
editor.execCommand("insertstring", data.meta);
}
}
};
})
You can change
var caption = item.value || item.caption || item.snippet;
line at https://github.com/ajaxorg/ace/blob/v1.1.8/lib/ace/autocomplete.js#L449 to
var caption = item.caption || item.value || item.snippet;
You can also implement custom insertMatch method on completer https://github.com/ajaxorg/ace/blob/v1.1.8/lib/ace/autocomplete.js#L181, but making a pull request with first option is better