Building an autocomplete textbox with ReactJS - javascript

My idea is to mantain the list of filtered users (suggestions) as state on the component, when the input changes, the state is updated.
How can I display the filtered list below the text box?
One option is 'datalist' tag (HTML5), but the list is already filtered, and part of the functionality of this tag is filtering.
I can't use any library or framework.
English is not my native language, sorry if you find some mistake.
Thanks.

Try a component from a design library, like the Material-UI autocomplete component http://www.material-ui.com/#/components/auto-complete
The dataSource attribute represents the array of autocomplete options.

How I did it was to pass in the dataList array as a prop and filterByField prop so that you can change what to filter, then add an event listener to the input (onChange) that passes the value to a function that filters the dataList.
onChangeInput(e) {
const { dataList, filterByField } = this.props;
const filteredDataList = dataList.filter(items => items[filterByField].toLowerCase().startsWith(e.target.value.toLowerCase()) );
// update internal component state to trigger render of dropdown list
this.setState({filteredList: filteredDataList});
}
I also added a check for no matches found so I can show a message:
if (filteredDataList.length === 0) {
this.setState({noMatchFound: true});
}
Then in my render() I simply check if filteredList isn't null and show an unordered list that I use css to display below the input.
{this.state.filteredList !== null
<ul className="autocomplete-list">
{this.filteredListMarkup()}
</ul>
}
filteredListMarkup() then uses map to return an <li> for each item with the necessary event handlers to update the selected item into the input and close the autocomplete-list by this.setState({filteredList: null});

You might also find this one useful:
https://github.com/reactjs/react-autocomplete

Even if you could use dependencies, I tried a bunch of the top current ones and personally wasn't happy with any of them (added dependencies like jQuery, not lightweight to use/understand/customize, css challenges, etc).
In then end, I found this lightweight vanilla React typeahead tutorial (no, I didn't write the tutorial). It's quick, simple, and three's no added dependency tree weight (eg: jQuery) or dependency maintenance. This solution also easily adjusted to the newer React patterns & the libraries I was using, and I'm guessing the same would be true of the patterns/libraries you may be using. Maybe this will help you or someone else like it did me.

Related

Unable to retrieve TinyMCE's content from a parent component

I have asked the question on their Github but no answer so far so I'm trying my luck here.
I'm creating a form that contains TinyMCE as component ; my goal is to be able to use and reuse this component through the whole website as the main Wysiwyg. I import it, give it a v-model and I'm good to go.
So, it "works", as in, I can set a value via the v-model and TinyMCE will display it nicely. However, it looks like I'm on a write only mode, because I can't extract the current data from it.
I originally created an example for the Github issue, you can see it here so that you have a good idea of what I'm facing. There's a textarea under the TinyMCE component that has the same v-model, just try to play with it and see the behavior.
While everything works if I declare a v-model inside the TinyMCE component, what do I need to do
get my value to the parent component?
Thanks in advance
You need to use v-model on the editor itself too or else it only receives the parent value but can't update it. However, you can't use the prop value for that because props can't be mutated.
Using a computed setter with v-model is a good solution here:
<editor
api-key="qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc"
v-model="model"
></editor>
Leave the value prop as is and add the computed:
computed: {
model: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
}
}
}
The computed setter returns the prop when getting, but emits rather than setting it.

MDC Components won't update UI when new values are added/ reset

I'm trying to reinitialize/ redraw all MDC components for form elements when their values change via javascript, but so far my attempts have fallen short. Is there an easy way to achieve this with a built in MDC method that I'm unaware of?
I created a custom way to reload the MDC components with a data-mdc-reload html attribute that fires on click but this isn't quite doing the job.
Here's a codepen showing the issue: https://codepen.io/oneezy/pen/XvMavP
click the UPDATE FORM VALUES button to add data
the VALUE output in red means the component is broke/ blue means it works
click the RESET button to reset data to initial state (this is broke too)
Javascript
// MDC Reload Component
function mdcReload(time = 1) {
var components = mdc.autoInit();
let reloadComponents = document.querySelectorAll('[data-mdc-reload]');
for (const reloadItem of reloadComponents) {
reloadItem.addEventListener("click", async () => {
setTimeout(function() {
components.forEach((c) => c.layout && c.layout());
}, time);
});
}
}
// Initialize MDC Components
mdcReload();
You can use the Foundations and Adapters provided by MDC.
I would suggest you make a MDC instance of every component and use the built in methods in mdc.COMPONENT.foundation_.adapter_.
Here is the modified pen
The issue was that you need to call floatLabel(true) to let the labels float after you set their values.
if (c.foundation_.adapter_ && c.foundation_.adapter_.floatLabel) {
c.foundation_.adapter_.floatLabel(true);
}
Furthermore I changed the select component to
// $(MDCselect).val('Option 3');
// Instantiate the MDC select component.
const mdcSelect = mdc.select.MDCSelect.attachTo(document.querySelector('.mdc-select'));
mdcSelect.foundation_.adapter_.setValue('Option 3');
Hope it helps !
I found that the easiest way to refresh the labels for the MDC components after setting a custom value via javascript is to send a Blur event to the input like this:
yourMDCInput.dispatchEvent(new Event('blur'))
This will leave the MDC component to decide which action it has to take, so it floats the label if there is a value set or resets the label if there is no value set.
It is quite annoying, but without digging into the code of the MDC foundation to find a better solution, I couldn't spot any better solution in the docs (which are incomplete and partly wrong anyways).
If you can, try using the MDC class instance to set your values - in that case, the MDC component is informed about the change and will behave as intended. When using autoInit, please note that the docs say the MDC class instance is attached to the root <div> while it is actually attached to the <label> where the data-mdc-auto-init attribute is set.
Assuming you wrap an MDCTextField in a <div id='my-text-field'>, you could do something like:
document.querySelector('#my-text-field label').MDCTextField.value = 'hello world'
and the field will update as expected.

combining 2 reducers ngrx angular 2

i have kinda big issue in my project, working with angular 2 (changeDetection onPush) & ngrx, my project has 3 dumb components and one smart component that managing them, its looks something like this:
Sorry for the UX, im not a designer
So i have 1 reducer for the Tags and the other one for the items, the lists has the same type of item its just depends if the item is finished or not to be included of each list.
Until now sounds good, but my problem is that the Tags can filter cross over the application and the lists filter of the search box effects just on the self list and the map, on the ngOnChanges of the lists im execute the search filter and emit to the smart component the filtered items, and the smart component dispatch to store to change the "isFiltered" property.
Where's my problem is ?
I cant manage both of the filters, 'cause i added "isFiltered" property on the type of item, but its effects different for the filters, i mean i have a combineLatest that managing the filter of items looks something like that:
Rx.Observable.combineLatest(
items,
tags,
(items, tags) => {
return items.map((item) => {
item.isFiltered = item.isFiltered && tags.includes(item.tagId);
return item;
})
})
its not working well, and its kinda problem cause its a routine that the refernce to the observable changed the ngOnChange jumps and calling the dispatch again, tryed alot of things, at first i added the UPDATE_TAGS action in the items reducer and handle it there, but i read that its not the best practice to listen to the same action in 2 reducers.
After that i tryed to handle it in the same reducer but i cant know which items filtered from the tags and which from the search box.
The only way i thought about and it will work is another property on the item of isFilteredTags, and isFilteredLists.
Let me know if u have an answer for me, its will help me.
Thank you.

React-Select: keep selected items in menu

Using React-Select in v1.0.0-beta10, I'd like to keep selected items in menu in order to be able to implement a dropdown behaving similar as the multi-select at MaterializeCss
here's a screenshot:
how to achieve this behavior?
Just to have this complete as you asked in react-select on GitHub this was made possible using removeSelected={false} in this pull request. Now (mid 2019) the solution is:
hideSelectedOptions={false}
You have access to a prop called filterOptions that accepts a function that takes the properties options, searchFilter and selectedOptions.
You should just be able to always return options that match the search filter instead of stripping out the selectedOptions something like below (if you are using underscrore/lodash. Or write your own method.
const filterOptions = (options, searchFilter, selectedOptions) => {
return _.filter(options, options => _.includes(option.value, searchFilter));
}
and then
<Select {...props} filterOptions={filterOptions} />
Hope this example helps.

Dynamic defaultValue on Combobox React Widgets

I'm fairly new to javascript and I'm currently making a web client that display some data a database. Pretty standard stuff I guess. I use react.js and I installed react-widgets to get some nice widgets. The comboxbox I use is getting it's data from a mongo database depending on the collection chosen from another comboxbox. So by changing the collection, the data inside is updated to reflect the change. This works well, but I can't seem to be able to set the default value. Here the code to render the comboxbox:
<Combobox onChange= {this.handleOnChange} data = {this.state.myValues} defaultValue= {this.state.myValues[0]}/>
I have had this issue with a couple of widgets that does not seem to be able to get updated dynamically. They work if all the data is there the first time I'm rendering (static data for example), but they don't if the data is empty at first and gets populated afterward. *I did make sure I call setState to call the render function.
Did I miss something? Is it a javascript issue that I don't understand?
Thanks!
Try wrapping the Combobox with a condition.
Something like this
{ this.state.myValues[0] &&
YOUR_TAG_COMBO
<Combobox onChange= {this.handleOnChange} data = {this.state.myValues} defaultValue= {this.state.myValues[0]}/>
}
I think you might be better off using the value prop, instead of defaultValue, it will give you more control over the component, and it will repopulate the form element when the state has changed.

Categories