How do you create an autocomplete input in Aurelia? - javascript

I am new to Aurelia, and am wondering how you can create an autocomplete input. I am trying to autocomplete for a panel of colors, and once you type into the input, it will autocomplete to the colors (items) that are in the panel.
This is the html:
<template>
<div>
<input type="text" value.bind="selectedColors1"
change.delegate="search()" id="filter" placeholder="Search for
feeds..">
<div>
<select multiple value.bind="selectedColors1" style="width:50%">
<option repeat.for="color of colors1" model.bind="color.id">
${color.name}
</option>
</select>
</div>
</div>
<br />
<button type="button" click.delegate="add()">Add</button>
<button type="button" click.delegate="remove()">Remove</button>
<br />
<select multiple value.bind="selectedColors2" style="width:50%">
<option repeat.for="color of colors2" model.bind="color.id">
${color.name}
</option>
</select>
</template>
And this is the JS:
export class dualList {
colors1 = [
{ id: "purple", name: "Purple" },
{ id: "black", name: "Black" },
{ id: "orange", name: "Orange" }
];
colors2 = [
{ id: "white", name: "White" },
{ id: "red", name: "Red" },
{ id: "blue", name: "Blue" }
];
selectedColors1 = [];
selectedColors2 = [];
add() {
this.selectedColors1.forEach(selected => {
// get the index of selected item
const index = this.colors1.findIndex(c => c.id === selected);
this.colors2.push(this.colors1[index]);
this.colors1.splice(index, 1);
});
}
remove() {
this.selectedColors2.forEach(selected => {
// get the index of selected item
const index = this.colors2.findIndex(c => c.id === selected);
this.colors1.push(this.colors2[index]);
this.colors2.splice(index, 1);
});
}
search(){
console.log(this.selectedColors1);
return true;
}
}
I expected the input id "filter" to autocomplete the colors in the first list, but nothing happens.

Related

How to Filter Quantity By Attribute in ReactJs

I'm attempting to create a quantity filter based on color, size, or both. When I click the red color, for example, it displays the whole quantity of the red color, but if I press color red and size small, it displays the exact quantity I require. Is there a way to accomplish this?
This is what I mean.
When I select a color or a size, the quantity should be displayed. Also, there should not be a duplication of my error, since there are three red colors listed above the image.
Code
import React, { useState } from "react";
export default function ControlledRadios() {
const [qty, setQty] = useState(0);
const data = [
{
id: 1,
name: "Product A",
attributes: [
{
id: 1,
color: "Red",
size: "Small",
qty: 200,
},
{
id: 2,
color: "Red",
size: "Medium",
qty: 100,
},
{
id: 3,
color: "Red",
size: "Large",
qty: 300,
},
{
id: 4,
color: "Yellow",
size: "Small",
qty: 200,
},
{
id: 5,
color: "Yellow",
size: "Medium",
qty: 100,
},
{
id: 6,
color: "Yellow",
size: "Large",
qty: 300,
},
],
},
];
const handleChange = (event) => {
setQty(event.target.value);
};
return (
<>
<h1>Quantity: {qty}</h1>
<fieldset value={qty} onChange={(event) => handleChange(event)}>
<h3>Color:</h3>
{data?.map(({ attributes }) => {
return attributes.map(({ id, ...rest }) => (
<>
<label key={id}>
<input
type="radio"
name="schedule-weekly-option"
value={rest.qty}
/>
{rest.color}
</label>
<br />
</>
));
})}
<h3>Size:</h3>
{data?.map(({ attributes }) => {
return attributes.map(({ id, ...rest }) => (
<>
<label key={id}>
<input
type="radio"
name="schedule-weekly-option"
value={rest.qty}
/>
{rest.size}
</label>
<br />
</>
));
})}
</fieldset>
</>
);
}
As seen in the data value, I have multiple color and sizes goes something like this for example {color: "Red", Size: "Small", qty: 200} I have multiple red values, so right every red should be added so when I click the Red radio button it should display the quantity 600 because the quantity of all the red will be added. but if I press like color Red and size Small it should display 200.
PS: If possible can u not make duplicate of colors like I did 3 color red and yellow, to make it only 1 Red and 1 Yellow same goes with the size.
If you need anymore clarification you need or explanation please comment down below. Thanks
Phew! This should do the trick. The idea is to pass more data to your handleChange handleChange - see how I changed it? I grab the data straight out of the target element, which is not ideal, but works.
I didn't do the filtering for you, good luck with that :) Should be just adding a (granted, a fairly complex) .filter(...) to your .map(...).
document.onreadystatechange = () => {
const {useState} = React;
function ControlledRadios() {
const [qty, setQty] = useState(0);
const data = [
{
id: 1,
name: "Product A",
attributes: [
{
id: 1,
color: "Red",
size: "Small",
qty: 200,
},
{
id: 2,
color: "Red",
size: "Medium",
qty: 100,
},
{
id: 3,
color: "Red",
size: "Large",
qty: 300,
},
{
id: 4,
color: "Yellow",
size: "Small",
qty: 200,
},
{
id: 5,
color: "Yellow",
size: "Medium",
qty: 100,
},
{
id: 6,
color: "Yellow",
size: "Large",
qty: 999,
},
],
},
];
const handleChange = (event) => {
const id = event.target.value;
const targetAttribute = data[0].attributes.find(x => x.id == id);
if (event.target.name === "schedule-weekly-option-color") {
let sum = 0;
data[0].attributes.forEach((a) => {
if (a.color===targetAttribute.color) {
sum += a.qty;
}
});
setQty(sum);
} else {
let sum = 0;
data[0].attributes.forEach((a) => {
if (a.color===targetAttribute.color && a.size===targetAttribute.size) {
sum += a.qty;
}
});
setQty(sum);
}
};
return (
<React.Fragment>
<h1>Quantity: {qty}</h1>
<fieldset value={qty} onChange={(event) => handleChange(event)}>
<h3>Color:</h3>
{data.map(({ attributes }) => {
return attributes.map(a => (
<label key={a.id}>
<input
type="radio"
name="schedule-weekly-option-color"
value={a.id}
/>
{a.color}
</label>
));
})}
<h3>Size:</h3>
{data.map(item => {
return item.attributes.map(a => (
<label key={a.id}>
<input
type="radio"
name="schedule-weekly-option-size"
value={a.id}
/>
{a.size}
</label>
));
})}
</fieldset>
</React.Fragment>
);
}
ReactDOM.render(<ControlledRadios />, document.body);
};
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>

Input field loos focus in Vue when has no results

I have Vue application. And inside, I have some input field. If this field has any results, the buttons forward and backward are visible, else not.
My problem is, that when I type inside input field, when I type something that has no results, input loose focus. (see snippet)
Hot to solve this?
new Vue({
el: '#app',
data: {
input: "",
items: [{
'id': 123,
'name': 'item1'
},
{
'id': 124,
'name': 'item2'
},
{
'id': 128,
'name': 'item3'
},
{
'id': 237,
'name': 'item4'
}
]
},
computed: {
search_list_of_workorders: function() {
var self = this;
var search_string = this.input.toLowerCase();
// apply filter
var array = this.search_filter_array(this.items, search_string);
return array.slice(0, 10).map(a => a.id);
},
number_of_search_results: function() {
return this.search_list_of_workorders.length
},
display_results_buttons: function() {
return this.number_of_search_results > 0
},
},
methods: {
search_filter_array: function(array, search_string) {
return array.filter(function(el) {
var id_filter = el.id.toString().includes(search_string);
return id_filter;
});
},
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<div id="app">
<button type="button" v-if="display_results_buttons">
Back
</button>
<div v-if="display_results_buttons">({{ number_of_search_results }})</div>
<input placeholder="Search" type="text" list="searchDropDown" id="searchInput" name="selectEventInput" v-model="input" />
<datalist id="searchDropDown">
<option v-for="(item, index) in search_list_of_workorders" :value="item" :key="`optionEvents_${index}`" >
</option>
</datalist>
<button type="button" v-if="display_results_buttons">
Forward
</button>
</div>
Use v-show instead of v-if. This will fix your issue perfectly!
The main difference:
v-if: Only renders the element to the DOM if the expression passes.
v-show: Renders all elements to the DOM and then uses the CSS display property to hide elements if the expression fails.
Use cases:
v-show: expensive initial load, cheap toggling,
v-if: cheap initial load, expensive toggling.
In your case, toggling is mandatory and it can be required many times, so v-show is a better solution. Also, it doesn't require re-rendering and will fix the focus-losing issue as well.
new Vue({
el: '#app',
data: {
input: "",
items: [{
'id': 123,
'name': 'item1'
},
{
'id': 124,
'name': 'item2'
},
{
'id': 128,
'name': 'item3'
},
{
'id': 237,
'name': 'item4'
}
]
},
computed: {
search_list_of_workorders: function() {
var self = this;
var search_string = this.input.toLowerCase();
// apply filter
var array = this.search_filter_array(this.items, search_string);
return array.slice(0, 10).map(a => a.id);
},
number_of_search_results: function() {
return this.search_list_of_workorders.length
},
display_results_buttons: function() {
return this.number_of_search_results > 0
},
},
methods: {
search_filter_array: function(array, search_string) {
return array.filter(function(el) {
var id_filter = el.id.toString().includes(search_string);
return id_filter;
});
},
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script>
<div id="app">
<button type="button" v-show="display_results_buttons">
Back
</button>
<div v-show="display_results_buttons">({{ number_of_search_results }})</div>
<input placeholder="Search" type="text" list="searchDropDown" id="searchInput" name="selectEventInput" v-model="input" />
<datalist id="searchDropDown">
<option v-for="(item, index) in search_list_of_workorders" :value="item" :key="`optionEvents_${index}`" >
</option>
</datalist>
<button type="button" v-show="display_results_buttons">
Forward
</button>
</div>

Display an array item's data based on the radio button that's being selected

I am trying to display the shoe array item's data if the color property matches the radio button color that is clicked. If the black radio button is selected, the array item with the black color property value should show.
(Note: How can I display or render the shoe data)
Thanks in advance!
App.js
import React, { Component } from "react";
class CheckColor extends Component {
constructor(props) {
super(props)
this.state = {
color: '',
shoes: [
{name: 'Black Shoe', color: 'Black', price: 180},
{name: 'Red Shoe', color: 'Red', price: 120},
{name: 'White Shoe', color: 'White', price: 100}
]
}
this.handleColorChange = this.handleColorChange.bind(this)
}
handleColorChange(e) {
const color = e.target.value
this.setState({ color: color })
}
render() {
const colors = ['Black', 'Red', 'White']
return(
<form>
{colors.map((color, index) =>
<label key={index}>
{color}
<input
value={color}
checked={this.state.color === color}
onChange={this.handleColorChange}
type="radio"
/>
</label>
)}
</form>
)
}
}
export default class App extends Component {
render() {
return (
<div className="App">
<CheckColor />
</div>
);
}
}
You can filter the colors based on the selected color values and then just use a map for showing the filtered shoes.
import React, { Component } from "react";
class CheckColor extends Component {
constructor(props) {
super(props);
this.state = {
color: "",
shoes: [
{ name: "Black Shoe", color: "Black", price: 180 },
{ name: "Red Shoe", color: "Red", price: 120 },
{ name: "White Shoe", color: "White", price: 100 }
]
};
this.handleColorChange = this.handleColorChange.bind(this);
}
handleColorChange(e) {
const color = e.target.value;
console.log(color);
this.setState({ color: color });
}
render() {
const colors = ["Black", "Red", "White"];
const shoesToShow = this.state.shoes.filter(item => ( item.color === this.state.color));
console.log(shoesToShow);
return (
<>
<form>
{colors.map((color, index) => (
<label key={index}>
{color}
<input
value={color}
checked={this.state.color === color}
onChange={this.handleColorChange}
type="radio"
/>
</label>
))}
</form>
{shoesToShow.map(shoe => (
<div>{shoe.name} </div>
))}
</>
);
}
}
Four Steps to display object as input radio dynamically
// 1. obj data
const phoneNums = {
jim: {
type: "Mobile",
value: 4153662323
},
tim: {
type: "Business",
value: 4153662323
},
}
//2. convert obj to array
const radioNumArr = Object.keys(phoneNums).map(i => phoneNums[i]);
// 3. Set data to User State via Hook
setRadioData(radioNumArr );
// 4.set hook and loop on radiodata
const [radioData, setRadioData] = useState([]);
<div>
{radioData.map((option) => (
<div className="forminputlabel" >
<input
type="radio"
name="dynamic-radio"
value={option.value}
/>
<label>{option.type} </label>
</div>
))}
</div>

React select v2 on space press selects first value

When on Select field I hit space, first value from options is being selected. How to disable such behaviour?
<Select
ref={r => (this.selectRef = r)}
className="basic-single"
classNamePrefix="select"
onInputChange={val => {
console.log('va', val)
this.setState({ inputValue: val })
}}
inputValue={this.state.inputValue}
options={[{ value: 'aaa', label: 'aaa bbb' }, { value: 'bbb', label: 'bbb ccc' }]}
name="color"
/>
HERE IS A DEMO
I would suggest to use the onKeyDown props and prevent the action when the use hits the space bar so nothing will be selected.
<Select
ref={r => (this.selectRef = r)}
className="basic-single"
classNamePrefix="select"
onInputChange={val => {
this.setState({ inputValue: val });
}}
onKeyDown={e => {
if (e.keyCode === 32 && !this.selectRef.state.inputValue) e.preventDefault();
}}
inputValue={this.state.inputValue}
options={[
{ value: "aaa", label: "aaa bbb" },
{ value: "bbb", label: "bbb ccc" }
]}
name="color"
/>
Live example here.

Vue js populate new selects when changing the main one

I need your help about populating or loading new select with vue js, I know how to do this with jquery but in vue i don't know how because i'm new with this library.
i have the main select :
<select>
<option value='3'>FRANCE</option>
<option value='5'>USA</option>
<option value='6'>CANADA</option>
<option value='8'>MOROCCO</option>
</select>
I want that if i choose FRANCE i get a select of FRANCE cities from database, and also when i select USA i get an other select of USA cities from database.
So for example i will get :
<select>
<option value='6'>CANADA</option>
<option value='8'>MOROCCO</option>
</select>
<select>
<option value='34'>France city one</option>
<option value='35'>France city two</option>
</select>
<select>
<option value='3'>Usa city one</option>
<option value='5'>Usa city two</option>
</select>
When choosing France and USA i will populate select of cities with an array
I appreciate any help, I don't realy know how can i do this with vue js,
I don't want add all select of cities in my html because i don't know how much countries i have.
I tried this but this not resolve my probleme :
const addProduct = new Vue({
el: '#addProduct',
data: {
name: '',
name_url: '',
cities: '',
countries: [],
range: 0
},
created: function () {
this.$http.get('/api/countries').then(response => {
this.countries = response.data
}, response => {
});
},
methods: {
addForm: function(val, data) {
this.range += 1;
alert(this.range)
var index = _.findIndex(this.countries,{city_id: val});
this.countries.splice(index, 1)
}
},
watch: {
'cities' (val, oldVal) {
this.$http.post('/api/cities/values', {city_id:val}).then(response => {
this.addForm(val, response.data);
}, response => {
});
}
}
});
in html :
<div class="uk-grid" data-uk-grid-margin>
<div class="uk-width-medium-1-4">
<label for="attribute">Countries</label>
<md-select name="country" id="country" v-model="country">
<md-option v-for="country in countries" :value="country.country_id">#{{ country.name }}</md-option>
</md-select>
</div>
</div>
<div class="uk-grid" data-uk-grid-margin>
<my-cities v-for="n in range"></my-cities>
</div>
<script type="x-template" id="my-cities">
<div class="uk-width-medium-1-4">
<label for="attr">Cities</label>
<md-select name="attr" id="attr" v-model="attr">
<md-option value="">Select </md-option>
<md-option value="val in values">Select</md-option>
</md-select>
</div>
</script>
an example like this on Jsfiddle : http://jsfiddle.net/pu8pp62v/3/
This is an example that you can maybe use (but need some modifications to use your API call) :
new Vue({
el: "#app",
data: function() {
return {
selectedCountries: [],
selectOptionsCountries: [
{ value: 3, name: 'FRANCE' },
{ value: 5, name: 'USA' },
{ value: 6, name: 'CANADA' },
{ value: 8, name: 'MOROCCO' }
],
selectedCities: [],
selectOptionsCities: []
}
},
methods: {
},
watch: {
selectedCountries: function(newValue, oldValue) {
this.selectOptionsCities = [];
this.selectedCities = [];
for( var i = 0, length = newValue.length; i < length; i++ ){
this.selectedCities[i] = [];
if( newValue[i] === 3 ){
this.selectOptionsCities.push(
[{ value: 31, name: 'Paris' },
{ value: 32, name: 'Marseille' }]
)
}
if( newValue[i] === 5 ){
this.selectOptionsCities.push(
[{ value: 51, name: 'New-York' },
{ value: 52, name: 'Boston' }]
)
}
if( newValue[i] === 6 ){
this.selectOptionsCities.push(
[{ value: 61, name: 'Montreal' },
{ value: 62, name: 'Vancouver' },
{ value: 63, name: 'Ottawa' },
{ value: 64, name: 'Toronto' }]
)
}
if( newValue[i] === 8 ){
this.selectOptionsCities.push(
[{ value: 81, name: 'Rabat' },
{ value: 82, name: 'Casablanca' },
{ value: 83, name: 'Fes' }]
)
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.5/vue.js"></script>
<div id="app">
Selected countries : {{ selectedCountries }}
<br />
Selected cities : {{ selectedCities }}
<br />
<select v-model="selectedCountries" multiple>
<option v-for="(option, index) in selectOptionsCountries" :value='option.value'>
{{ option.name }}
</option>
</select>
<select v-for="(optionsCities, index) in selectOptionsCities" v-model="selectedCities[index]" multiple>
<option v-for="(option, index) in optionsCities" :value='option.value'>
{{ option.name }}
</option>
</select>
</div>
Added after author's comment:
Check this fiddle: http://jsfiddle.net/jjpfvx5q/1/
Inside 'chosenCities' array you have all selected cities by country (one city per country.)
Original answer:
Here is an example for you: fiddle
Is that what you are trying to achieve?
setTimeout functions are just pretending a real data fetching.
<script src="//unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<template>
<div>
<select v-model="country">
<option disabled value="">Please select one</option>
<option
v-for="c in countries"
:value="c.id">{{ c.name }}</option>
</select>
<span>Selected: {{ country }}</span>
<span v-if="cities.length">Cities:</span>
<ul v-if="cities.length">
<li v-for="c in cities">{{ c }}</li>
</ul>
</div>
</template>
</div>
<script>
var Main = {
data() {
return {
country: {},
countries: [],
cities: [],
coInit: [{ id: '3', name: 'France' }, { id: '2', name: 'USA' }],
cFrance: ['Paris', 'Whatever'],
cUSA: ['NY', 'LA']
}
},
methods: {
loadCountries: function () {
setTimeout(() => { this.countries = this.coInit }, 500);
},
getCities: function() {
if(this.country) {
switch (this.country) {
case '3':
setTimeout(() => { this.cities = this.cFrance }, 500);
break;
case '2':
setTimeout(() => { this.cities = this.cUSA }, 500);
break;
}
}
}
},
mounted() {
this.loadCountries();
},
watch: {
country: function() {
this.getCities();
}
}
}
var Ctor = Vue.extend(Main);
new Ctor().$mount('#app');
</script>

Categories