Here is the current code (I didn't write it just trying to work with it)-
<select type="text" id="{{for}}" name="{{for}}" ng-model="model.value" class="form-control input-sm" placeholder="{{placeholder}}" ng-options="c.name as c.name for c in countries track by c.code">
<option value="">— Select —</option>
</select>
and the countries array seems to be set up like so
$http.get('services/dictionary/countries').then(function(response) {
_.chain(response.data).map(function(country) {
return {
code: country.id,
digraph: country.digraph,
trigraph: country.trigraph,
name: country.name
};
}).sortBy('name').each(function(country) {
dictionary.countries.push(country);
});
deferred.resolve(dictionary.countries);
}, function(error) {
$log.error('dictionary:', error);
});
We are pulling the list of countries from some dictionary service, and trying to display them as select options. (we aren't really using digraph and trigraph here, but it's needed for other areas that use the same dictionary call).
Problem 1 is, we can set the choice and it saves, but the select list will not show the object I saved, especially after I store and refresh it.I assume this is because we aren't properly setting the value="" with the ng-options we've set up, but I can't get it to work properly after trying many iterations (don't really understand the documentation).
Problem 2 is, on this and all other select dropdowns we have, I can't figure out a way to revert to a null choice. I have a value="" default option but it doesn't blank out the ng-model when selected. we need this to allow for user screw ups, 'oops i didn't even mean to set that field.' type things.
Much appreciated for the help gang.
Problem 1: Are you setting the ngModel value to the saved value on refresh? Only then will it be selected on the refresh.
Problem 2: Set the model to an empty object in your controller:
model = {};
Related
I am working on an AngularJS 1.0 application and facing an issue with Angular Select.
Just Copying a dummy code:
<select ng-model ="data.state" ng-options="data.states">
Select
Here, it's listing states of a country and passing a default data as ng-model.
I need to show a text "Select" if default is not matching with the data source.
But it's not working as expected and it's showing an extra empty space instead of select.
Thanks
"I need to show a text "Select" if default is not matching with the data source." In this case your data.state may be empty (ie "") case. You may be expecting some logic like this:
<label>States : </label>
<select ng-model ="data.state" ng-options="data for data in data.states" >
<option value="" selected hidden >SELECT</option>
<!--Removes the Empty space and shows "SELECT" as pre-populated by default in the drop-down. -->
</select>
<br>Selected State : {{data.state}}
Empty space appears as a placeholder offered by Angular to hold text like "Select an option" or "Choose a best answer". You can override this using the <option value="" selected hidden >Select</option> which won't appear in the selected drop down options list as it is hidden.
If you wish to have the select appear as an option for user to choose in order if the field can be remained empty and is not mandatory you can use the following code instead:
<option value="" selected >SELECT</option>
In controller expecting the data as:
// $scope.data.state = something that is returned from some API response
$scope.data = {
states: [
"Kerala",
"Karnataka",
"Andhra",
"Haryana"
]
}
Checkout the JS Fiddle here for working sample.
<select ng-model ="data.state" ng-options="data.states">
make use of track by in ng-repeat. track by $index or id if any
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.
I have a form in AngularJS where the options for one of the dropdowns depends on what is selected in the first.
<select ng-options="obj.name for obj in institutions track by obj.id" ng-model="newUser.institution">
</select>
<input type="text" name="email" ng-model="newUser.email">
<!-- DEPENDENT on what is selected in the previous dropdown -->
<select ng-options="obj.name for obj in organizations track by obj.id" ng-model="newUser.associatedOrg">
</select>
Institutions are all loaded right off the bat from available institutions in the database. That is done in the controller like this:
function populate() {
return Institutions.all().then(function (institutions) {
$scope.institutions = institutions;
return institutions;
});
}
// at the end of the controller
populate();
However, those "organizations" in the second dropdown are based on their parent table institution, so I need to do something like $scope.organizations = institution.memberOrganizations; in the controller after an option from the first dropdown is selected.
For now, just to make sure things work, I have made a button called "Load Organizations" with an ng-click for this function:
$scope.getOrganizationsOnCampus = function(institution) {
$scope.organizations = institution.memberOrganizations;
};
That works, however that is a really bad user experience.
So my question is: How do I update $scope.organizations every time a new institution is selected?
I want to do this without listening to the DOM - like you would in jQuery, because I know that is way against the AngularJS best practices.
P.S. For further clarity, here is a screenshot of before, and after that "Load Organizations" button is clicked, to load the child organizations of the selected institution. This is what I want to automatically do every time a different institution is selected in the previous option.
BEFORE
AFTER
Would you use ng-change like this:
<select ng-options="obj.name for obj in institutions track by obj.id" ng-model="newUser.institution" ng-change"updateOrg(newUser.institution)">
</select>
and then in your controller, you have a function like this:
$scope.updateOrg = function(institution) {
$scope.organizations = institution.memberOrganizations;
};
I am trying to hardcode in an option to an ng-options select list when the scope it is pulling from has no answers. I was able to achieve this in another scenario by doing something like that:
ng-show="!$scope.length"
However, it does not seem to be working inside the select list itself. Perhaps it's something with how Angular does ng-options? Here is my attempt:
<select ng-model="myModel" ng-options="option.title for option in optionsList">
<option ng-show="!optionsList.length">There are no prompts for this lesson.</option>
</select>
I have hardcoded an option inside of the select with the same logic, but it does not seem to want to work. Thanks in advance!
One way to do this would be to create a filter that inserts the default option if empty...
.filter('defaultOptionIfEmpty', function () {
return function (items, defaultItem) {
return items.length ? items : [defaultItem];
}
});
Use it like this...
<select ng-model="foo" ng-options="item.value for item in items | defaultOptionIfEmpty : {id: 0, value: 'There are no prompts for this lesson.'}">
</select>
Fiddle
Assuming that your data for options is "dataBlocksHere.currentData.selections", you need to pre-process this block to check if there is anything in it, and insert the appropriate content into it.
So before you assign the item to scope, process the results in your javascript.
if(!dataBlocksHere.currentData.selections){
// insert item into selections, something like
dataBlocksHere.currentData.selections['data'] = {id: 0, data: "There are no prompts for this lesson."};
}
There are several questions very similar to this one yet I have been unable to come up with a solution.
I have a select list using angularJS. I need to use the title attribute so I have an ng-repeat to create the options, there is always a blank option even if I use ng-selected to always select the first option.
Even if I make a selection and the blank option goes away, if I then filter out that selected value the blank will reappear.
I have included a select list using ng-option (which does not include my needed tittle attribute) and a default value to show that the blank will appear after filter.
The behavior I desire would be to never have a blank option (always selecting first option would be fine) and to possibly have a directive per option for special handling of click events.
Thanks in Advance!
JS Fiddle: http://jsfiddle.net/32DFM/3/
<select size="3" ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm"
ng-selected="$first"
value="{{p}}"
title="{{p.name}}">
{{p.name}}
</option>
</select>
I forked your fiddle (if I may be so blunt): http://jsfiddle.net/XsFe8/2/
This fixes it somewhat. Although I haven't gotten it to work properly together with the filter.
Anyway, what I do here, is to use the person.id as the value on each option.
<select ng-model="person.current">
<option ng-repeat="p in people | filter:person.SearchTerm" ng-selected="$first" value="{{p.id}}" title="{{p.name}}">
{{p.name}}
</option>
</select>
And set the initial calue on the person.current model:
$scope.person.current = $scope.people[1].id;
But it's still not 100% though. I'm a bit stumped to why the blank spaces appear when you filter the select....
An alternative that might or might not work, would be to use something like ng-repeat="p in filterPeople() and filter your array in a filterPeople function. But I'm not sure if this will change anything.
UPDATE: I tested out my suggestion above, here: http://jsfiddle.net/XsFe8/2/
If you set the selected object to be the first object in the filtered array, it works. I do this each time a new filtered array is created:
$scope.filterPeople = function () {
var array = filterFilter($scope.people, $scope.person.SearchTerm);
$scope.person.current = array[0].id;
return array;
};
It looks like things get hairy when another object than what is visible in the select is actually selected. This is kind of understandable :)
Your actual problem is the value in ngModel is referencing a value which doesn't exist in the select anymore.
A solution is to whenever you alter the select options, you also check the person.current to ensure that it points to a valid entry.
This also implies that you might want to move your filter into the controller, and set the options in the scope (you can use the $filter service in your code to get same behaviour there, https://docs.angularjs.org/api/ng/filter/filter). This way you can have a function in your controller checking if person.current is valid, and if not, set it to desired options (e.g. the first one).
the hairyness cited above is due to an empty array when all items are filtered out and is fixed by:
if(array.length>0)
$scope.person.current = array[0].id;
http://jsfiddle.net/b0z6vpr8/
Hope this helps