array observable with content observable and jqAutocomplete - jquery-ui

I'm using Knockout 3 with the plugin jqAutocomplete by Ryan Niemeyer. I have a problem with this model:
var ViewModel = function() {
var self = this;
self.myOptionsObs = ko.observableArray([
{ id: ko.observable(1), name: ko.observable("item 1 o"), description: ko.observable("item label 1 o") },
{ id: ko.observable(2), name: ko.observable("item 2 o"), description: ko.observable("item label 2 o") },
{ id: ko.observable(3), name: ko.observable("item 3 o"), description: ko.observable("item label 3 o") }
]);
self.myValueObs = ko.observable();
};
ko.applyBindings(new ViewModel());
<input data-bind="jqAuto: { source: myOptionsObs, value: myValueObs, inputProp: 'name', template: 'itemTmpl' }" />
As you can see, there is an observable array and each element is also an observable.
The autocomplete don't work well. As you can see in this Fiddle, the left column has an observable array but its elements aren't observable. If you click in the left box and write something, a list of options appear.
But in the right column, you have the same, but the element's are all observable. If you click in the right box and write something, when the list appear, if you move the cursor up and down, you could see that the row 'name' gets deleted and filled with zeros.
What I have to change in my data-bind attribute?
This question is related with this question.
I have to say that this solution works ok for me. But the updated plugin don't.
Thanks !!

The jqAutoComplete plugin isn't setup to work with observable properties (although it could be enhanced to do so without much work).
For now, I think that your best bet is to create a computed that will always return a plain and up-to-date version of your options.
self.myOptionsObs.plain = ko.computed(function() {
return ko.toJS(self.myOptionsObs);
});
Sample: http://jsfiddle.net/rniemeyer/45cepL9b/
I'll try to take a look at some point about supporting observable properties. Shouldn't take many changes.

Related

Need assistance reading the object returned by getRowId of MaterialReactTable

I am using MaterialReactTable in my application and following the Row Selection Option as outlined at this link: https://www.material-react-table.com/docs/guides/row-selection
The table is working fine and I am able to select the row I want and it returns the correct id but returns it in the format: rowSelection = {63d19bebc764a5587a48683a: true}. I am not familiar with this format.
I have tried everything I know but am unable to parse out the id from the object.
Please provide suggestion to parse out the id or suggest changes to make this solution work.
I have tried the other methods of row selection suggested on the page (useRef and '#tanstack/react-table') and could not get either to work so would like to stick to this method as I feel it is close.
Below is the code and options I am using with the MaterialReactTable
return (
<MaterialReactTable
columns={columns}
data={data}
enableRowSelection
onRowSelectionChange={setRowSelection}
enableMultiRowSelection={false}
//getRowId={(row) => row?._id }
getRowId={(originalRow) => originalRow._id}
initialState={{ showColumnFilters: true,
columnVisibility:
{ _id: false } }} //hide columns listed to start }}
manualFiltering
manualPagination
manualSorting
muiToolbarAlertBannerProps={
isError
? {
color: 'error',
children: 'Error loading data',
}
: undefined
}
muiTableBodyRowProps={({ row }) => ({
//add onClick to row to select upon clicking anywhere in the row
onClick: row.getToggleSelectedHandler(),
sx: { cursor: 'pointer' },
})}
onColumnFiltersChange={setColumnFilters}
onGlobalFilterChange={setGlobalFilter}
onPaginationChange={setPagination}
onSortingChange={setSorting}
rowCount={rowCount}
state={{
columnFilters,
globalFilter,
isLoading,
pagination,
showAlertBanner: isError,
showProgressBars: isRefetching,
sorting,
rowSelection
}}
/>
);
Given the format of the response, rowSelection = {63d19bebc764a5587a48683a: true}, I had originally assumed a key: value pair with the id being the key. My initial attempts to parse out the id as the key had failed. After trying a number of different options, I was able to use the Object.keys() function as follows:
console.log(Object.keys(rowSelection)); //used to view the key(s) returned
setCurrentRoom(Object.keys(rowSelection));
This code converted the id to a string in an array as follows: currentRoom = ['63d19bd9c764a5587a486836']

Is setting a multiple mat-select dropdown value to objects possible?

Using mat-select in multiple mode, is it possible to pre-set the selected values if the values that are part of the select options are objects?
For example if the options in the select are as follows
toppingList: any[] = [
{ id: 1, description: 'Extra cheese' },
{ id: 2, description: 'Mushroom' },
{ id: 3, description: 'Onion' },
{ id: 4, description: 'Pepperoni' },
{ id: 5, description: 'Sausage' },
{ id: 6, description: 'Tomato' }
];
Then can you pre-set the selected values for that mat-select as such?
this.toppings.setValue([{ id: 1, description: 'Extra Cheese' }]);
Taking this stackblitz as an example you see that the the form values are getting set as desired, but the select buttons in the drop down itself are not showing as checked.
Is this even possible, or am I doing something wrong?
as i know for equality of two object, for example in java, you need write equal / hashcode for detect what object equal to another one.
you bind select value to object and typescript could not understand what compare object together. so i change your sample and bind value of options to id and it works!
see this stackblits

Twitter typeahead only showing some items returned by bloodhound

I'm using Bloodhound to fetch data from the database, then twitter typeahead to display the options below a search box.
Currently, the bloodhound part is finding the objects required, but the typeahead is not displaying them.
var artist_retriever = new Bloodhound({
// turns input query into string of tokens to send to database.
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
// URL to fetch information from
url: "/artists?query=%QUERY",
wildcard: '%QUERY',
// Manipulate the array of artists returned, for display to user.
transform: function(array_of_artists){
// array of artists is returned from DB.
// Put each artist into a readable string
array_of_artists = create_artist_descriptions(array_of_artists)
console.log(array_of_artists)
// Returns correctly:
// [
// { artist: "Joe" },
// { artist: "Bob" },
// { artist: "Smith" },
// { artist: "Tom" },
// ]
return array_of_artists
}
},
// turns return value into a string of results, with this 'key' before each result.
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('artist'),
});
// display:
// instantiate the typeahead UI
// https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md
searcher = $('.typeahead').typeahead(
// options:
{
hint: false
},
// datasets:
{
// Where to get data: User the bloodhound suggestion engine:
source: artist_retriever.ttAdapter(),
// Which attribute of each result from the database should be shown:
displayKey: 'artist',
templates: {
notFound: new_artist_option_template(),
footer: new_artist_option_template()
}
}
)
Update
It turns out that there's a weird bug in typeahead. It only seems to work with the "limit" attribute set to a maximum of 4. If you set "limit" to 5, the typeahead gives you nothing.
searcher = $('.typeahead').typeahead(
// options:
{
hint: false
},
// datasets:
{
// Where to get data: User the bloodhound suggestion engine:
source: artist_retriever.ttAdapter(),
// Which attribute of each result from the database should be shown:
displayKey: 'signature',
limit: 4, // This can do a max of 4! Odd.
templates: {
notFound: new_artist_option_template(),
footer: new_artist_option_template()
}
}
This issue has been solved. Please see update 2 directly.
I have reproduced this issue in this JSFIDDLE.
As you said, its a bug. You also reported that this bug goes away if you do limit:4.
Actually on my end, or in the FIDDLE, I have experienced that this issue comes when the number of results returned = value in limit.
To test this issue in the FIDDLE, do the following:
Note: Searching for 1947 returns exactly 5 rows.
When limit is set to 4:
Searching for 1947 returns 4 results.
When limit is set to 5:
Searching for 1947 returns nothing.
When limit is set to 6:
Searching for 1947 returns one 1 result - the first result.
Hence if you keep the limit set to 1 less than the actual number of results returned, then this will keep on working.
I have also submitted this issue in their github page. I will be keeping track of this issue and will keep updating this answer as need be.
Update 1:
Found a similar question on SO here. "Luciano García Bes" seems to have figured the solution. Please direct all upvotes there.
Basically he says:
It's counting the number of rendered hints before appending them, so
if the number of hints equals the limit it'll append an empty array.
To prevent this I just switched lines 1723 and 1724 so it looks like this:
that._append(query, suggestions.slice(0, that.limit - rendered));
rendered += suggestions.length;
Update 2:
This issue has been fixed on pull 1212. Closing our own issue 1312. The bug was corrected the same way discussed in update 1.

Select2 version 4 not not able to set data / selection

I just upgraded to version 4 and now my set data is not working
I'm doing the following, which worked fine before the update
Init
$("#fertilizer").select2({
data: listToLoad,
placeholder: mf('pleaseSelectFertilizer',"please select fertilizer")
}).on('change', function (e) {
var concentration = $("#fertilizer").select2('data')[0].concentration;
$("#typesOfConcentration").text(concentration);
$("#typesOfConcentrationDiv").removeClass("hide");
});
var fertilizer = $("#orders").select2('data')[0].fertilizer;
var fertilizerId = $("#orders").select2('data')[0].id;
var concentration = $("#orders").select2('data')[0].concentration;
$("#fertilizer").select2("data", {id: fertilizerId, text:fertilizer});
As noted in the release notes (twice actually), .select2("data") is read-only now. This will actually trigger a warning if you put Select2 into debug mode (setting the option debug to true).
In your case, you don't need to use .select2('data') at all. You appear to only be using it so you can re-map fertilizer to text, which should be done way before the option is selected. The id and text properties are required and it doesn't take much to re-map them before passing data to Select2.
var listToLoad = $.map(listToLoad, function (obj) {
obj.text = obj.text || obj.fertilizer;
return obj;
});
$("#fertilizer").select2({
data: listToLoad,
placeholder: mf('pleaseSelectFertilizer',"please select fertilizer")
}).on('change', function (e) {
var concentration = $("#fertilizer").select2('data')[0].concentration;
$("#typesOfConcentration").text(concentration);
$("#typesOfConcentrationDiv").removeClass("hide");
});
For everyone else who actually used .select2('data'), you should be able to use .val() now and just pass in the id that needs to be set. If you need to select an option which doesn't actually exist, you can just create the <option> for it (like you would in a standard <select>) ahead of time.

extjs4 grid - changing column editor per row basis

ExtJS4 grid anticipates appropriate editor (cellEditor or rowEditor) per column.
If a column's header field is dateField - date selector will be applied on every row in that column.
What I need is an editor with different field editors per row, not per column.
The Extjs3 solution is provided here - unfortunately doesn't fit in Extjs4 case.
(please check that link to see explanatory images, cause I can't post images yet)
There's also a single column solution called property grid, but again - it supports only one column and is very deviated from the standard Ext.grid component
I have tried manually changing grid editor by customizing column.field and reloading grid.editingPlugin.editor, but always get a blank rowEditor panel with no fields.
//by default rowEditor applies textField to all cells - I'm trying to force custom numberFiled on apropriate row
var numberField=Ext.form.field.Number();
grid.columns[0].field=numberField;
//destroy current rowEditor's instance
delete grid.editingPlugin.editor;
//now, upon doubleClick on apropriate cell it should reinitialize itself (initEditor()) - and it does, but is an empty panel
what am I missing here? once I delete editingPlugin.editor everything should start from the beginning like during the first time rowEditor is called, but it looses all the fields
Solution for Ext4:
I was looking for a solution for this and this guy said the property grid has this behavior.
I have adapted it to work in a clean way for me
on initComponent I declared:
this.editors = {
'date' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Date', {selectOnFocus: true})}),
'string' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Text', {selectOnFocus: true})}),
'number' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}),
'int' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}),
'boolean' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.ComboBox', {
editable: false,
store: [[ true, 'Sim' ], [false, 'Não' ]]
})})
};
I used these functions to help me (copied):
this.renderCell = function(val, meta, rec) {
var result = val;
if (Ext.isDate(val)) {
result = me.renderDate(val);
} else if (Ext.isBoolean(val)) {
result = me.renderBool(val);
}
return Ext.util.Format.htmlEncode(result);
};
this.getCellEditor = function(record, column) {
return this.editors[record.get('type')];
};
And finally, associate these functions to the column:
{text: "Valor", name : 'colunaValor', width: 75, sortable: true, dataIndex: 'valor', width:200,
renderer: Ext.Function.bind(this.renderCell, this),
getEditor: Ext.Function.bind(this.getCellEditor, this)
}

Resources