I have tried to follow the examples on the Angular 1.4.12 docs and what I've found here, still I cannot set a default value for a select menu.
My html (using controller as of mob):
<select name="userCurrencyType"
ng-model="mob.currencyType"
ng-options="s.name + ' - ' + s.code for s in mob.currencyTypes"></select>
Which correctly gives me a menu like:
Where my json is an array of objects:
[{ "code": "USD", "name": "United States Dollar" },
{ "code": "GBP", "name": "United Kingdom Pound" }...]
I want the default menu item to be the first item (USD). I have tried setting the ng-model to mob.currencyType and setting this in the controller both like:
_this.currencyType = _this.currencyTypes[0];
and
_this.currencyType = { "code": "USD", "name": "United States Dollar" }
Neither approach gives me a default value set. What am I missing?
UPDATE
After some good suggestions from other users, and some experimenting, it would seem the problem was my data service call was not returning a promise:
_this.currencyTypes = MockDataFactory.query({ filename: 'currency_codes' });
So I added
_this.currencyTypes.$promise.then(function () {
init();
});
And then
function init () {
_this.currencyType = _this.currencyTypes[0];
}
You can set using ng-init
<select ng-model="currency" ng-init="currency='United States Dollar'" ng-options="code.name as code.name for code in currencyTypes">
</select>
DEMO
What you have is fine, just include a track by:
<select name="userCurrencyType"
ng-model="mob.currencyType"
ng-options="s.name + ' - ' + s.code for s in mob.currencyTypes track by s.code"></select>
Related
Sorry, the title isn't worded very well. I'm using a category choose to choose a category from an API. I currently get the list of categories, filter through their names, and display them in the category chooser. When the user clicks submit, I want the to parse through the API and find the id associated with that category name. Here's an example output from the API:
{
"_id": "5c2fde414502d923ceafaa30",
"title": "Category 2",
"description": "My second category, testing 123",
"createdAt": "2019-01-04T22:29:21.047Z",
"updatedAt": "2019-01-04T22:29:21.047Z",
"__v": 0
},
Here's the code I use for the Category Chooser:
JS:
$.getJSON("http://localhost:2672/categories", function (json) {
$('#category-chooser').empty();
$('#category-chooser').append($('<option>').text("Choose a Category"));
$.each(json, function (i, obj) {
$('#category-chooser').append($('<option>').text(obj.title));
});
});
HTML
<select id="category-chooser" class="form-control" name="category">
<option selected="selected">blank</option>
</select>
If you store the json returned from getJSON somewhere outside the callback, your submit button would fire off something like below:
function getCategoryId(){
const categoryChooser = document.getElementById('category-chooser');
const categorySelected = categoryChooser.value;
json.forEach(entry => {
if(entry.title === categorySelected){
return entry["_id"];
}
});
}
Originally in my Vue component I had a series of nested if statements that would go through the JSON data to determine whether a text input should be displayed or a select based on a has_selectable_value option being true (select display) or false (text input display), and if it was a select then loop through the data and output associated options.
I have been able to change that to a computed statement which almost does everything I need it to do apart from one little thing which is to display the select options.
Here is the relevant part of the Vue Code:
<template v-else-if="searchtype == 9">
<select v-for="service in selectableServices" class="form-control" v-model="searchvalue" required>
<option value="">Select A Location</option>
<option v-for="sl in selectableLocations" :value="sl.location_id">{{sl.name}}</option>
</select>
<input v-for="service in nonSelectableServices" class="form-control" v-model="searchvalue" placeholder="Enter Search Value" required>
</template>
The current computed functions:
services: function () {
var ret = []
this.countries.forEach(function(country) {
country.states.forEach(function(state) {
state.services.forEach(function(service) {
ret.push(service)
});
});
});
return ret;
},
selectableServices: function () {
return this.services.filter(service => service.id == this.service && service.has_selectable_location);
},
nonSelectableServices: function () {
return this.services.filter(service => service.id == this.service && !service.has_selectable_location);
},
selectableLocations: function () {
// Filter one more level down
return this.selectableServices.map(service => service.selectablelocations);
},
This is the JSON data structure I am working with as well (I cut it back to the relevant parts for this part of the code):
[
{
"id": 1,
"name": "Country Name",
"states": [
{
"id": 1,
"name": "State Name",
"services": [
{
"id": 1,
"name": "Service Name",
"has_selectable_location": 1,
"selectablelocations": [
{
"id": 1,
"name": "Selectable Location A",
},
]
}
]
}
]
}
]
Using a Vue plugin for Chrome I can see that the computed function selectableLocations loads an array containing the individual locations, but the existing v-for statement isn't able to function correctly. Instead I still need to go down one more level which I can do by adding an extra v-for loop like so:
<template v-for="selectableLocationsList in selectableLocations" >
<option v-for="sl in selectableLocationsList" :value="sl.location_id">{{sl.name}}</option>
</template>
Everything displays correctly, but I am not sure if this is best practice as I was hoping to do as much of this in a computed function as possible and ideally only require a single v-for statement. But if it's not possible like that I understand and I can leave it as is.
Thank you in advance.
Edit: After more testing and research I have come up with this code that works as I had desired:
var formArray = []
var locationsArray = this.servicesArray.filter(service => service.id == this.service);
locationsArray.map(service => service.selectablelocations);
locationsArray.forEach(function(selectableLocations) {
selectableLocations.selectablelocations.forEach(function(location) {
formArray.push(location)
});
});
return formArray;
Is there a way I can refactor this further and make it a bit cleaner?
Solely considering the code you posted after the Edit , the code can be refactored this way:
let formArray = [];
formArray = this.servicesArray
.filter(service => service.id == this.service)
.map(service => service.selectablelocations)
.reduce((prev, curr) => prev.concat(curr))
return formArray
Note that the map you used doesn't do anything as Array.prototype.map only returns a new array, but you didn't assign it to anything.
I have a dropdown menu that contains some links for various section of the page. The application is written with AngularJS version 1.4, the dropdown menu does works, but when I enter the page directly through the url in the dropdown menu is always selected the empty voice instead of the correct one. Here' the code:
HTML
<select ng-options="menu_voice.name for menu_voice in menu_voices track by menu_voice.url" ng-model='selectedOption' ng-change="changeLink()">
</select>
JS:
$scope.changeLink = function(){
$state.go($scope.selectedOption.url);
};
$scope.menu_voices = [
{
"url": 'account.company',
"name": 'Company'
},
{
"url": 'account.billing',
"name": 'Billing'
},
{
"url": 'account.password',
"name": 'Password'
},
{
"url": 'account.design',
"name": 'Design'
},
{
"url": 'account.social',
"name": 'Social'
},
{
"url": 'account.notifications',
"name": 'Notifications'
}
];
If I select a voice in the dropdown menu, I go to the correct link with the correct voice selected. But if in the url bar I enter something like:
www.myapp.com/account/billing
I go to the correct page but in the dropdown menu the selected voice is empty.
How can I solve this?
EDIT after first reply:
I added this:
var name = $window.location.pathname;
name = name.substring(9);
name = name.charAt(0).toUpperCase() + name.slice(1);
var url = $window.location.pathname.substring(1).replace(/\//g, '.');
$scope.getSelectedFromUrl = function(){
$scope.selectedOption = {"name": name, "url": url};
};
If I print in the console
console.log($scope.selectedOption);
I get the correct object, e.g:
Object {name: "Design", url: "account.design"}
In the html I simply added the ng-init:
<select ng-options="menu_voice.name for menu_voice in menu_voices track by menu_voice.url" ng-model='selectedOption' ng-change="changeLink()" ng-init="selectedOption = getSelectedFromUrl()">
</select>
But nothing changed.
You could use the ng-init directive to call a function that parses the route and matches it to an item from the voices array. Then set the selectedOption model to that array item, which will set the select option.
ng-init="selectedOption = getSelectedFromURL()"
I have simple model that submits a form that are all from a select I am using an ng-repeat like so:
'Ctrl'
isdom.scheduleOptions = ['Pass', 'N/A'];
'html'
<select ng-model="isdom.isdomForm.isDom101">
<option ng-repeat="option in isdom.scheduleOptions" value="{{option}}">{{option}}</option>
</select>
The person who has built the api end point is asking for the data in this format:
"outcomes": [
{ "itemNo": "is11", "outcome": "Pass" }
,
{ "itemNo": "is12", "outcome": "Pass" }...
How can I do this when my model is like so?
{
"isDom11": "N/A",
"isDOm12": "Pass",...
}
I thought about try to get all the elements in the model that start with isDom and pushing them into an outcomes array that has been modified into objects to copy the format required.
Is there a different way I can use ng-repeat to achieve this?
You could use ng-options for populating the select.
See: ngOptions or select
So it should be something like this:
$scope.isdom.scheduleOptions = [
{ "itemNo": "is11", "outcome": "N/A" }
,
{ "itemNo": "is12", "outcome": "Pass" }
];
<select ng-model="isdom.isdomForm.isDom101"
ng-options="item as item.outcome for item in isdom.scheduleOptions track by item.itemNo"></select>
Try using the (key, value) syntax as given in angular docs.
Key value in ng-repeat
(key, value) in expression –
where key and value can be any user defined identifiers, and expression is the scope expression giving the collection to enumerate.
For example: (name, age) in {'adam':10, 'amalie':12}.
Your example,
isdom.scheduleOptions = {
"isDom11": "N/A",
"isDOm12": "Pass",...
}
<select ng-model="isdom.outcomes">
<option ng-repeat="(itemNo, outcome) in isdom.scheduleOptions" value="{{outcome}}">{{outcome}}</option>
</select>
I have this angular select:
<select ng-model='obj.status' ng-options='status.code as (status.code + " " + status.phrase) for status in status_codes.data track by status.code'>`
My $scope.status_codes is like this:
data: [
{
"code":"100",
"phrase":"...",
"spec_title":"RFC7231#6.2",
"spec_href":"http://tools.ietf.org/html/rfc7231#section-6.2"
}
...
]
My $scope.obj.status is updated to "300" or "100" or whatever as I change my select, but the select display is always blank. So, the model updates to the selected value of the select input but the input does not show the currently selected value, it shows a blank item.
If i change ng-options to be ng-options='status as (status.code ...' it works, but I only want status.code in my model, not the whole status array. What gives?
I have {{obj | json }} in my HTML and it reads:
obj = {
"name": "",
"description": "",
"payload": "",
"status": "200",
"responseHeaders": {
"entry": [
{
"key": "",
"value": ""
},
{
"key": "",
"value": ""
}
]
}
}
Remove track by.
From the Docs:
Be careful when using select as and track by in the same expression.
My best guess is that the as uses a normal value, like "300", but the track by is using a typed value, like "int:300". Removing one or the other should do it, preferably the track by.
They are giving this as an example:
This will work:
<select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
but this will not work:
<select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
According to the docs here: https://docs.angularjs.org/api/ng/directive/ngOptions:
select as label for value in array
So in your example this should work (you will get code value as select value in model):
status.code as (status.code + " " + status.phrase) for status in status_codes.data
track by should be used when you have object as value in model, array of objects for options and you want to match current model value to one of objects in array.