<select value={author} onChange={({ target }) => setAuthor(target.value)} >
{authors.map(author =>
<option key={author.name} value={author.name}>
{author.name}
</option>)}
</select>
This is in React/NodeJS.
Everything works as expected, and the first option/author.name is displayed by default. However, it is not being set as the default value for the React state, and clicking on the dropdown and then selecting it does not change this. If another option is selected and then you select the first option, it works as expected.
You need to set the state (author in this case) for the first time manually, because the onChange event only fires when you change the selected option, so it does not set the state when it loads. you have two option:
If authors known before hand you can use this:
const [author,setAuthor] = useState(authors[0].name)
If you load authors asynchronously you can wait for them to load and then set the author using useEffect:
useEffect(()=>{
if(authors && authors.length>0){
setAuthor(authors[0].name);
},[authors])
This actually seems rather simple and it seems like I am close to getting it right but it is not working properly - still learning! :))
This is some context regarding this quest and it seems to work there.
I am getting data from the Countries REST API and I am setting the state by using
const [countries, setCountries] = useState([]);
Now Ive got a dropdown menu where I want to display the countries using the selected value which is a region from the list of regions.
<form>
<select
name="regions"
id="inputRegion"
onChange={filterByRegion}
>
<option value="Africa">Africa</option>
<option value="Americas">Americas</option>
<option value="Asia">Asia</option>
<option value="Oceania">Oceania</option>
<option value="Polar">Polar</option>
</select>
</form>
const filterByRegion = (e) => {
handleSubmit(e.target.value);
e.preventDefault();
};
function handleSubmit(value) {
const filteredByRegionCountries = countries.filter(
(country) => country.region === value
);
setCountries(filteredByRegionCountries);
On the region button itself I am calling onChange={filterByRegion}
After one selects one region it displays the data properly but after you try to use another region it displays no countries at all. I am thinking it has something to do with setCountries(filteredbyRegionsCountries)?
Why is setCountries not being updated properly even though we are saying that it should be updated with whatever regions is selected by using the handleSubmit() function?
Does anybody have any insight? Merci and thank you in advance!
By using
countries.filter((country) => country.region === value);
you filter out all countries that don't have the name of the selected country.
See the https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter reference.
When you first select "Europe", for instance, you filter out everything that isn't Europe and when you select another country, let's say you select "Polar", you filter out "Europe" as well, because it filters out everything that isn't "Polar".
You could try logging out the countries array to the console after each change.
So I have an odd behavior that I want to implement on my dropdown. I have two scenarios and both will have different default selected values.
Scenario 1: If user role is admin, then dropdown will not be disabled and 'Select Location' placeholder will show up as selected default.
Scenario 2: if user role is clerk, then dropdown will be disabled and the selected value will be a specific location from the dropdown selection.
While I got the role permission and disabled/enabled thing setup, I'm not sure how to dynamically change the selected value of the dropdown. This is inside reactive forms form group, so not sure I can use ngModel or can I?
Here's my dropdown:
<select [ngModel]="null" formControlName="location" required >
<option value="null" disabled>{{'SelectLocation' | translate}}</option>
<option selected *ngFor="let store of location" [ngValue]="store._storeKey">{{ store._storeName }}</option>
</select>
Here's the check I have that disables/enables the dropdown:
checkUserPermissions() {
if (this.userPermissions._userPrivilegeKey === 100) {
//TODO: Default to store2 of list for example
this.transactionForm.controls['location'].value = stores._store; // This is right?
this.transactionForm.controls['location'].disable();
} else if (this.userPermissions._userPrivilegeKey === 200) {
//TODO: Default to select location placeholder (currently working)
this.transactionForm.controls['location'].enable();
}
}
You should use the patchValue function instead of directly assigning the value when dealing with forms
this.transactionForm.controls['location'].patchValue(stores._store)
I have two dropdown in my example with reset state (select bank select state).only first dropdown have data .When I changed first dropdown value then i fetch state data and show in dropdown . if i select YES BANK it show me select state .Now if I select any state example Delhi.then do something.But Now If I change again bank instead of yes bank example Axis bank state dropdown show Delhi why ? it show be reset and show select state ?
how to reset second dropdown (when first dropdown is change).here is my code
https://codesandbox.io/s/7wlwx2volq
Second dropdown always show select state with new data of bank,when user change bank name
onChangeDropdown = e => {
console.log(e.target.value);
this.props.callbackFn(e.target.value);
};
please explain
One possible solution is: Instead of using the selected property on options, use controlled component means use the value property of select field. Whenever any change happen to bank list, reset the value of state select filed value. Also define the value='' with default option.
Like this:
<select value={this.props.value} onChange={this.onChangeDropdown}>
<option value='' disabled>
{this.props.defaultOption}
</option>
{makeDropDown()}
</select>;
Pass value from parent component, like this:
<DropDown
value={this.state.bankName}
data={this.state.bankData}
defaultOption="select Bank"
callbackFn={this.callStateService}
/>
<DropDown
value={this.state.stateName}
data={this.state.stateData}
defaultOption="select State"
callbackFn={this.callDistrictService}
/>
Define onChange function for state select field change:
callDistrictService = value => {
this.setState({ stateName: value });
}
Working Sandbox.
A view of my AngularJS app makes heavy use of ng-repeat directive. It is done like this:
<div ng-repeat="branches in company">
<p>{{branches.name}}</p>
<p>{{branches.location}}</p>
<div>
<select ng-model="branches.officeInformationType">
<option ng-repeat="offices in branches">{{offices.type}}</option>
</select>
<select ng-model="branches.officeInformationMeters">
<option ng-repeat="offices in branches">{{offices.meters}}</option>
</select>
<select ng-model="branches.officeInformationColor">
<option ng-repeat="offices in branches">{{offices.color}}</option>
</select>
</div>
</div>
The fact is, the second ng-repeat and and the others after it (offices in branches) are actually the same everytime, so it wouldn't need to be recalculated for every branch. It would need to be binded to the row it belonges to, for saving it later, so the branches.officeInformation model should still be watched by angular, but I would like to make the whole program more performant.
I am using angular-ui-router and when I change the view between my "Choose your office" view and any other, the lag is tremendous, almost at a minute of wait time when you leave the "Choose your office" page. It renders fast enough, 2 seconds for the whole rendering, but when I leave the page it takes a ton of time to change to the other view.
Any ideas, taking into consideration that the ng-model binding "branches.officeInformation.." is of importance?
EDIT: I have tried remove the nested ng-repeats and for each ng-repeat that I removed, the transition between states got faster and faster. When I removed all the nested ng-repeats the transition became instantaneous, hence why I believe it has to do with the ng-repeats.
The ng-repeats are tracked by $index and where possible I used :: for one time binding.
Thanks.
We can lazy load a dropdown's options right before the user interacts with it.
First, we initialize each dropdown with only the selected option, so you can see it when the dropdown is closed.
Then we attach an ng-focus directive to each dropdown. When our callback fires we can:
fully populate the options for that dropdown
remove all but the selected option from the previously active dropdown
I wasn't entirely sure of the structure of your data (it looks like some arrays have additional properties on them). So I chose to create "view model" objects that represent the UI. You can adapt this to your own structure.
Controller:
// Set up some test office options (null for no selection)
var allOffices = [null];
for (var i = 0; i < 50; i++) {
allOffices.push(i);
}
// activeDropdown holds the dropdown that is currently populated with the full list
// of options. All other dropdowns are only populated with the selected option so
// that it shows when the dropdown is closed.
var activeDropdown;
$scope.company = [
// Branch 1
[
// These objects represent each dropdown
{
// Just the selected option until the user interacts with it
options: ["0"],
selected: "0"
}, {
// Just the selected option until the user interacts with it
options: ["1"],
selected: "1"
}, {
// Just the selected option until the user interacts with it
options: [null],
selected: null
}
],
// Branch 2
[
// These objects represent each dropdown
{
// Just the selected option until the user interacts with it
options: ["2"],
selected: "2"
}, {
// Just the selected option until the user interacts with it
options: ["3"],
selected: "3"
}, {
// Just the selected option until the user interacts with it
options: [null],
selected: null
}
]
];
// When the user interacts with a dropdown:
// - fully populate the array of options for that dropdown
// - remove all but the selected option from the previously active dropdown's
// options so that it still shows when the dropdown is closed
$scope.loadOffices = function (dropdown) {
if (activeDropdown === dropdown) {
return;
}
dropdown.options = allOffices;
if (activeDropdown) {
activeDropdown.options = [activeDropdown.selected];
}
activeDropdown = dropdown;
};
Template:
<div ng-repeat="branch in company">
<div ng-repeat="dropdown in branch">
Selected: {{ dropdown.selected }}
<select ng-focus="loadOffices(dropdown)" ng-model="dropdown.selected">
<option ng-repeat="o in dropdown.options">{{ o }}</option>
</select>
</div>
</div>
Note that ng-focus was the only directive I needed to apply to each dropdown when I tested this. But you may need to add ng-keydown, ng-mouseover, ng-click, or others to get it to work in all scenarios including mobile.
I also noticed a potential styling issue. When you focus on a dropdown, we load all of the options for that dropdown. This may cause the width of the dropdown to change, so if you can set the same width for all of them you should be good.
If the number of options in each dropdown is huge, we may be able to optimize even further by writing some custom directives that interact and allow the actual DOM element options to be shared. But I suspect we won't have to go that far for this example.
Have you tried 'track by $index' ? it will reduce angular watches overhead.
something like that:
div ng-repeat="branches in company track by $index">
<p>{{branches.name}}</p>
<p>{{branches.location}}</p>
<div>
<select ng-model="branches.officeInformationType">
<option ng-repeat="offices in branches track by $index">{{offices.type}}</option>
</select>
<select ng-model="branches.officeInformationMeters">
<option ng-repeat="offices in branches track by $index">{{offices.meters}}</option>
</select>
<select ng-model="branches.officeInformationColor">
<option ng-repeat="offices in branches track by $index">{{offices.color}}</option>
</select>
</div>
</div>
First and foremost, thanks to those that helped me find the answer.
The problem was that I nested too many ng-repeats with too many event handlers attached to each repeated element. ng-models, ng-changes and ng-clicks were really heavy, but the number of elements was also out of control.
I solved this by using a single select without any nested ng-repeats, this select (and the options) are in a modal view, so a different controller. From that controller I return the select results, having only one select for all the elements in the page. When the data is returned from the modal, I use it from the main controller of the view.
Thanks again.