React - value of select element not changing with state change - html

Im working on a project where when creating a user i have to pick city,institution and department. When you pick city you get a list of institution that are in that city and when you pick institutions you get departments. What i want to achieve is when you pick another city, institution and department reset to default value. And the problem is that it doesn't work.
Here is the code:
<select
autoComplete="off"
name="institution"
value={caregiverState.institution}
onChange={(e) => {
handleChange(e);
handleDepartmentQuery(e);
}}
>
{institutions.map((institution: IInstitution, index: number): ReactElement => {
return (
<option key={index} value={institution._id}>
{institution.name}
</option>
);
})}
<option value="" selected disabled hidden>
Select institution
</option>
</select>
This is state and handleChange method:
const [caregiverState, setCaregiverState] = useState<ICaregiver>({
name: '',
description: '',
email: '',
city: '',
institution: '',
department: '',
role: 'caregiver',
});
const handleChange = (e): void => {
switch (e) {
case e.target.name === 'city':
return setCaregiverState({
...caregiverState,
institution: '',
department: '',
});
case e.target.name === 'institution':
return setCaregiverState({
...caregiverState,
department: '',
});
default:
return setCaregiverState({
...caregiverState,
[e.target.name]: e.target.value,
});
}
};
Problem is even if i change state to "" select element does't change it to :
<option value="" selected disabled >
Select institution
</option>
I actually get the firstinstitution from map()
Thanks in advance!

Related

Angular table filter clearing group

I have 2 filters on a user list. A user can select a group containing members and use the search filter to search by last name. When the user backspaces a user to look for another, this resets the groups to all users. I need this to only show the users in the selected group.
TS
updateFilter(event) {
const val = event.target.value.toLowerCase();
const temp = this.temp.filter(function (d) {
return d.lastName.toLowerCase().indexOf(val) !== -1 || !val;
});
this.rows = temp;
if (this.table) {
this.table.offset = 0;
}
}
onGroupSelected($event) {
const groupId = $event.target ? $event.target.value : $event;
if (groupId === 'none') {
this.rows = this.temp;
} else {
const groupUsers = this.groupUserMap.get(groupId);
if (groupUsers) {
this.rows = this.temp.filter((serviceUser) =>
groupUsers.includes(serviceUser.id));
} else {
this.rows = [];
}
}
// #ts-ignore
this.userSelections = this.userSelections ? this.userSelections : {};
this.userSelections.groupId = groupId;
localForage.setItem(this.username, this.userSelections);
}
HTML
<input
type='text'
class="form-control w-200px"
placeholder='Search by Last Name...'
(keyup)='updateFilter($event)'
/>
<select class="form-control w-200px" (change)="onGroupSelected($event)">
<option value="none">All service users</option>
<option *ngFor="let group of groups"
[value]="group.id"
[selected]="userSelections.groupId === group.id">
{{group.name}}
</option>
</select>
You can use ngModel with tow-way binding, to save and manipulate search filters:
<select
class="form-control w-200px"
[(ngModel)]="selectedGroup"
(change)="onGroupSelected()"
>
<option value="none">All service users</option>
<option *ngFor="let group of groups" [value]="group.id">
{{ group.name }}
</option>
</select>
<input
type="text"
class="form-control w-200px"
placeholder="Search by Last Name..."
[(ngModel)]="search"
(keyup)="updateFilter()"
/>
And in order not to lose your users table you can create a copy which will be filtered and displayed.
public initialUsers = [
{ id: 100, groupId: 1, name: 'foo' },
{ id: 101, groupId: 2, name: 'bar' },
{ id: 102, groupId: 1, name: 'john' },
{ id: 103, groupId: 2, name: 'doe' },
{ id: 104, groupId: 2, name: 'baaar' },
{ id: 105, groupId: 1, name: 'fooodoe' },
];
public filteredUsers = [];
ngOnInit(): void {
this.filteredUsers = this.initialUsers;
}
Here is a demo on stackblitz, I used a list to go fast but It's just display. You just have to replace <ul> <li></li> </ul> by your <table> ... </table>
If you would want to take an observable way of doing this, then I would suggest to make a form of your controls, ooooor just use 2 form controls instead. I chose form here as it wraps it up nicely with both form controls (search and dropdown):
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
search: [''],
group: [0] // "all" option as initial id
})
}
Then we would listen to when the form value changes and assign the filtered data to a variable, here named filteredUsers$.
this.filteredUsers$ = this.form.valueChanges.pipe(
startWith(this.form.value), // to trigger initially
// 'this.users' refers to your original users array
map((value: any) => {
// 'all' option is chosen, just filter based on search
if (value.group === 0) return this.users.filter(x => x.lastName.toLowerCase().includes(value.search))
// filter by group and search
return this.users.filter(x => {
return (x.groupId === value.group) && (x.lastName.toLowerCase().includes(value.search.toLowerCase()))
})
})
)
That is it, then we just iterate filteredUsers$ in the template:
<tr *ngFor="let user of filteredUsers$ | async">
Of course we need the form in the view and it would look like this:
<form [formGroup]="form">
<input
type="text"
placeholder="Search by Last Name..."
formControlName="search"
/>
<select formControlName="group">
<option *ngFor="let group of groups" [ngValue]="group.id">
{{ group.name }}
</option>
</select>
</form>
Here is a DEMO with the above code

How to display the components based on category selection

Here I have written a Vue.js code. So here I have multiple templates so for multiple templates I have written multiple components.
So now I have to display each components based on selected category.
For example in the select option if i select single family homes then step2 component should display if i select real state then step3 components should displayed.
And also how to display the entered value in preview page
*vuejs*
<script>
Vue.component(
'step1',
{
template: '#step1',
props: [
'currentStep',
'step1'
]
}
);
Vue.component(
'step2',
{
template: '#step2',
props: [
'currentStep',
'step2'
]
}
);
Vue.component(
'step3',
{
template: '#step3',
props: [
'currentStep',
'step1',
'step2'
]
}
);
Vue.component(
'step4',
{
template: '#step4',
props: [
'currentStep',
'step1',
'step2'
]
}
);
var app = new Vue(
{
el: '#app',
data: {
currentStep: 1,
step1: {
category: '',
title: '',
address: '',
city:'',
state:'',
Zip:'',
price:'',
description:'',
},
step2: {
bedrooms_p: '',
bathrooms_p: '',
square_ft_p: '',
lot_size_p: '',
total_rooms_p: '',
stories_p: '',
year_built_p: '',
h_o_a_p: '',
garages_p: '',
basement_p: '',
roof_p: '',
exterior_p: '',
cooling_p: '',
heating_p: '',
schools_p: '',
image_p: '',
image2_p: '',
image3_p: '',
image4_p: '',
},
},
ready: function() {
console.log('ready');
},
methods: {
goToStep: function(step) {
this.currentStep ++;
},
stepsback: function(step) {
this.currentStep --;
},
}
}
);
</script>
<select>
<option>single family homes</option>
<option>real state</option>
<option>IT industry</option>
</select>
Here is the solution, although it has arbitrary values, as your example code is not complete.
<step1
currentStep="some_value"
step1="another_value"
v-if="'step1' === select_value"
/>
<step2
currentStep="some_value"
step2="another_value"
v-if="'step2' === select_value"
/>
<step3
currentStep="some_value"
step1="another_value"
step2="yet_another_value"
v-if="'step3' === select_value"
/>
<step4
currentStep="some_value"
step1="another_value"
step2="yet_another_value"
v-if="'step4' === select_value"
/>
The variable select_value is the selected value of the select element in your HTML. To use it you have to bind the value of the select in the Vue model.
For example you could use inside your Vue app something like that:
var app = new Vue(
{
el: '#app',
data: function () {
return {
select_value: ''
// rest of your data properties
}
}
// rest of your component functionality
}
);
and then change your select element to something like that:
<select v-model="select_value">
<option value="step1">single family homes</option>
<option value="step2">real state</option>
<option value="step3">IT industry</option>
<option value="step4">Some Other Option</option>
</select>
By making those changes you will have at the end the result you need.
Try out to give a value to your options then using dynamic component :
<select v-model="select_value">
<option value="1">single family homes</option>
<option value="2">real state</option>
<option value="3">IT industry</option>
<option value="4">Some Other Option</option>
</select>
and :
<component currentStep="some_value" step1="another_value" step2="yet_another_value" :is="`step${select_value}`" />

ReactJS problem with defaulting value on select tag

Trying to set the default value on a select tag in a react component but I cannot get it to work:
const ComboBox = (props) => {
const options = [
{ id: 1, value: "SHOES" },
{ id: 2, value: "SOCKS" },
{ id: 3, value: "SHIRTS" },
{ id: 4, value: "BELTS" }
];
return (
<React.Fragment>
<label htmlFor={props.id}>{props.label}</label>
<select id={props.id} value={props.selected} onChange={props.changeHandler}>
{options.map((row, ndx) => (
<option key={ndx} value={row.id}>{row.value}</option>
))}
</select>
</React.Fragment>
);
};
Always selects the first option in the list.
Provided an example of the behavior here: CodeSandBox
Pls replace below code. use value={props.value} not props.selected
<select
id={props.id}
value={props.value}
onChange={props.changeHandler}
>
{options.map((row, ndx) => (
<option key={ndx} disable={ndx === 0 ? "true" : false} value={row.id}>
{row.value}
</option>
))}
</select>
You are trying to set default value to 1 then you can do it using usestate in react.
here is the code.
import React, { useState } from "react";
import "./styles.css";
const ComboBox = (props) => {
const options = [
{ id: 1, value: "SHOES" },
{ id: 2, value: "SOCKS" },
{ id: 3, value: "SHIRTS" },
{ id: 4, value: "BELTS" }
];
return (
<React.Fragment>
<label htmlFor={props.id}>{props.label}</label>
<select id={props.id} value={props.value} onChange={props.changeHandler}>
{options.map((row, ndx) => (
<option key={ndx} value={row.id}>
{row.value}
</option>
))}
</select>
</React.Fragment>
);
};
export default function App() {
const [selectedValue, setSelectedValue] = useState(1);
const comboHandler = (e) => setSelectedValue(e.target.value);
return (
<div className="App">
<ComboBox
id="test"
name="test"
value={selectedValue}
changeHandler={comboHandler}
></ComboBox>
<div>
<h2>Value Should Be {selectedValue}</h2>
</div>
</div>
);
}
Just note the default usestate value is the first one
const [selectedValue, setSelectedValue] = useState(1);
You can change the value inside useState(here) to make the default selected value.

How to populate Select control with ID value retrieved from JSON file using HTTPClient

In my Angular app, I am populating the below select control with values retrieved from a JSON file using HTTP Client.
<label for="manager">Manager:</label>
<select class="form-control" id="manager">
<option value="" disabled>Choose manager</option>
<option *ngFor="let manager of managers" [value]="manager.id">{{manager.fullName}}</option>
</select>
Here is the JSON:
"managers": [
{
"id": 1,
"fullName": "Phil",
"contactPreference": "email",
"email": "phil#gmail.com",
"phone": "0865963625"
}
]
Here is where I am creating the form:
this.jobForm = this.fb.group({
title: ['', Validators.required],
description: ['', Validators.required],
managerId: [''],
});
Here is ajob object that I have:
job: IJob = {
id: null,
title: '',
description: '',
employeeId: null,
managerId: null
};
I want to populate the managerId of this job object with manager.Id from the select option
Here is my attempt, but it is not working:
this.job.managerId = this.jobForm.value.manager;
Can someone please tell me how I can access the ID from the select control & add it to my job object?
I also printed console.log('MANAGER VALUE: ' + this.jobForm.value.manager); but this is what is printed:
MANAGER VALUE: undefined
OK, so I figured out what I needed to change.
I added fomControlName to the select control.
HTML:
<select class="form-control" id="managerId" formControlName="managerId">
<option value="" disabled>Choose manager</option>
<option *ngFor="let manager of managers" [value]="manager.id">{{manager.fullName}}</option>
</select>
The formControlName above needs to match managerId in the FormBuilder below.
Typescript:
this.jobForm = this.fb.group({
title: ['', Validators.required],
description: ['', Validators.required],
managerId: [''],
});
this.job.managerId = this.jobForm.value.managerId;

Populate select options based on another select with Vue and JSON

Need help creating a dynamic search form with select options for Districts, Regions and locations.
Regions select must be populated based on the District and Locations based on the Regions
The data is stored on a JSON file with the following structure:
[
{
"level": 1,
"code": 1,
"name": "District"
},
{
"level": 2,
"code": 101,
"name": "Region"
},
{
"level": 3,
"code": 10101,
"name": "Location"
}
]
here´s the complete JSON file:
https://gist.github.com/tomahock/a6c07dd255d04499d8336237e35a4827
html snippet
<select name="district" v-model="district">
<option value=''>Select District</option>
<option v-for="district in filterDistricts" :value="district.code">
{{ district.name }}
</option>
</select>
<select name="region" v-model="region">
<option value=''>Select Region</option>
<option v-for="region in filterRegions" :value="region.code">
{{ region.name }}
</option>
</select>
<select name="location" v-model="location">
<option value=''>Select Location</option>
<option v-for="location in filterLocations" :value="location.code">
{{ location.name }}
</option>
</select>
javascript snippet
data() {
return {
searchData: [],
districts: [],
regions: [],
locations: []
}
},
created(){
this.fetchData();
},
computed: {
filterDistricts() {
return this.districts = this.searchData.map(res => ({
level: res.level,
code: res.code,
name: res.name
}))
.filter( res => res.level === 1)
},
filterRegions() {
return this.regions = this.searchData.map(res => ({
level: res.level,
code: res.code,
name: res.name
}))
.filter( res => res.level === 2)
},
filterLocations() {
return this.locations = this.searchData.map(res => ({
level: res.level,
code: res.code,
name: res.name
}))
.filter( res => res.level === 3)
}
},
methods: {
fetchData(){
axios.get('http://localhost:8000/json/searchData.json')
.then((response) => (
this.searchData = response.data
))
.catch((err) => {
console.log(err)
})
}
}
I think I need to associate de code numbers, but I can´t figure out how.
Any ideas?
Thanks
First, I wouldn't bother with those map calls since you're only reproducing the same structure.
Second, I'll assume that each sub-element (region / location) relates to its parent (district / region) via a pattern where each sub's code is prefixed with the parent code, followed by a two-digits, zero-padded.
With that in mind, try this in your computed properties
filterDistricts () {
return this.searchData.filter(({ level }) => level === 1)
},
filterRegions () {
// assuming you don't want any selections until a district is chosen
if (!this.district) return []
const codeCheck = new RegExp(`^${this.district}\\d{2}$`)
return this.searchData.filter(({ level, code }) =>
level === 2 && codeCheck.test(code))
},
filterLocations () {
if (!this.region) return []
const codeCheck = new RegExp(`^${this.region}\\d{2}$`)
return this.searchData.filter(({ level, code }) =>
level === 3 && codeCheck.test(code))
}
Extra notes...
From looking at your template, it seems you should initialise your data as
data () {
return {
searchData: [],
district: null,
region: null,
location: null
}
}
Computed properties don't need to be stored in data properties so you don't need districts, regions and locations.
Your label options should also be disabled so they cannot be selected, eg
<option disabled value="">Select Region</option>