AngularJS - How to make a cascading select and save to API - javascript

I have the following JSON file that lists Countries and their states :
[
{
"value": "Australia",
"text": "Australia",
"states": [
{
"value": "Australian Capital Territory",
"text": "Australian Capital Territory"
},
{
"value": "New South Wales",
"text": "New South Wales"
}
]
}
]
I load the data in a variable called countriesAndStates and then I display this on the frontend :
<div class="form-group">
<label for="country" class="col-sm-2 control-label">Country</label>
<div class="col-sm-3">
<select class="form-control" id="country" data-ng-model="project.country" data-ng-options="country.value as country.text for country in countriesAndStates">
<option disabled value="">Select</option>
</select>
</div>
</div>
<div class="form-group">
<label for="country" class="col-sm-2 control-label">State</label>
<div class="col-sm-3">
<select class="form-control" id="state" data-ng-model="project.state" data-ng-options="state.value as state.text for state in countriesAndStates.states" data-ng-disabled="!countriesAndStates.states">
<option disabled value="">Select</option>
</select>
</div>
</div>
I'm also loading data from a REST API to display it on the frontend :
var detailProject = Restangular.one('projects', $routeParams.projectId);
detailProject.get().then(function (project) {
$scope.project = project;
$scope.saveProject = function () {
project.put().then(processSuccess, processError);
};
});
Once I click on save, a saveProject function puts back the data to the API.
Now... How do I link up the states with the Countries?
From what I understood in the documentation, I would need to change the ng-model properties to reflect country and state... like so :
<select id="country" ng-model="states" ng-options=""></select>
<select id="state" ng-disabled="!states" ng-options=""></select>
But I'm a bit confused, how can I do that when I want the API data to be loaded in the select boxes?

I think something like this would work....
You can filter off the options by project.country. I also placed a ng-disabled directive to prevent them from selecting an option in this dropdown if they haven't selected a country first.
<select ng-disabled="!project.country" ng-model="selectedState"
ng-options="state.value as state.text for state in countriesAndStates.states | filter:{text: project.country}"></select>

After searching for hours... I finally figured it out. Posting this for everyone who might encounter the same issue.
I modified my data, splitting countries and states into 2 different files
Countries:
[
{ "name": "United States" },
{ "name": "Canada" }
etc...
]
States:
[
{ "name": "Alabama", "country": "United States" },
{ "name": "Alberta", "country": "Canada" }
etc...
]
Modified HTML
<select data-ng-model="project.country" data-ng-options="country.name as country.name for country in countries" data-ng-change="updateCountry()" class="form-control">
<option disabled value="">Select country</option>
</select>
<select data-ng-disabled="!project.country" data-ng-model="project.province" data-ng-options="state.name as state.name for state in states | filter:{country: availableStates}" class="form-control">
<option disabled value="">Select state</option>
</select>
Controller
$scope.updateCountry = function () {
$scope.availableStates = $scope.project.country; // filter by country
$scope.project.province = ''; // reset the province field to empty
};

Related

Basic read of json fails

It's very simple. I have 2 selects. When the user selects the category, it will appear another select with a subcategory.
The problem is that subcategory select is always empty.
Category JSON (vm.categories):
[
{
"doctype":"1120",
"description":"bla bla",
"subcategories":[
{
"#id":1,
"subcategory":"1",
"description":"New Offer",
"templates":[
{
"template":"12",
"description":"asfafasga",
"deliveryChannels":[
]
}
]
},
{
"#id":2,
"subcategory":"2",
"description":"New Offer",
"templates":[
{
"template":"1121",
"description":"asfag",
"deliveryChannels":[
{
"deliveryType":"4",
"description":"Test"
}
]
}
]
}
]
}
]
HTML
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="vm.selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option> <!-- not selected / blank option -->
<option value="{{category}}"
ng-repeat="category in vm.categories">
{{category.description}}
</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<!-- Sub Categories -->
<label ng-show="vm.selectedCategory" for="subcategory-select"><b>Sub-Category </b></label>
<select name="subcategory-select"
ng-show="vm.selectedCategory"
ng-model="vm.selectedSubCategory">
<option value="" disabled>--- Please select a sub-category ---</option> <!-- not selected / blank option -->
<option value="{{subCategory}}"
ng-repeat="subCategory in vm.selectedCategory.subcategories">
{{subCategory.description}}
</option>
</select>
</div>
</div>
Any idea why this is happening? Somehow I can't read the subcategories array from the selected category
EDIT: If I put <span>{{vm.selectedCategory}}</span> in my html, it shows the json above. but if I put <span>{{vm.selectedCategory.subcategories}}</span> it's null
Another edit: Even <span>{{vm.selectedCategory.doctype}}</span> is null
It is because your selected value is not evaluated as object but as string. To handle this issue you can:
You can either convert selected string to object with ng-change directive on first select and the following function
$scope.convert = function(){
$scope.vm.selectedCategory = angular.fromJson($scope.vm.selectedCategory)
}
or use ng-options
<select name="mySelect" id="mySelect" ng-options="category.description for category in data" ng-model="vm.selectedCategory">
</select>
Demo
To make a working demo, had to simplify vm.[namedVariables] into simple variables: kindly check below
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.selectedCategory = '';
$scope.categories = [{
"doctype": "1120",
"description": "bla bla",
"subcategories": [{
"#id": 1,
"subcategory": "1",
"description": "New Offer",
"templates": [{
"template": "12",
"description": "asfafasga",
"deliveryChannels": [
]
}]
},
{
"#id": 2,
"subcategory": "2",
"description": "New Offer2",
"templates": [{
"template": "1121",
"description": "asfag",
"deliveryChannels": [{
"deliveryType": "4",
"description": "Test"
}]
}]
}
]
}];
/*
for(var i=0; i<$scope.profiles.length; i++){
console.log($scope.profiles[0].TravelerExtended);
console.log($scope.profiles[0].TravelerExtended.ExtendedInt_1);
}
*/
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat='item in categories'>
{{item.description}}
<div ng-repeat='subC in item.subcategories'>
{{subC.subcategory}} - {{subC.description}}
</div>
</div>
<hr/>
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option>
<!-- not selected / blank option -->
<option value="{{category.doctype}}" ng-repeat="category in categories">{{category.description}}</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<!-- Sub Categories -->
<label ng-show="selectedCategory" for="subcategory-select"><b>Sub-Category </b></label>
<ng-container ng-repeat="item in categories">
<!--
<p>(1) {{selectedCategory}} </p>
<p>(2) {{item.doctype}} </p>
-->
<select name="subcategory-select" ng-show="selectedCategory == item.doctype" ng-model="vm.selectedSubCategory">
<option value="" disabled>--- Please select a sub-category ---</option>
<!-- not selected / blank option -->
<option value="{{subCategory}}" ng-repeat="subCategory in item.subcategories">
{{subCategory.description}}</option>
</select>
</ng-container>
</div>
</div>
</div>
Instead of using the value attribute with interpolation, use the ng-value directive:
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="vm.selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option> <!-- not selected / blank option -->
̶<̶o̶p̶t̶i̶o̶n̶ ̶v̶a̶l̶u̶e̶=̶"̶{̶{̶c̶a̶t̶e̶g̶o̶r̶y̶}̶}̶"̶
<option ng-value="category"
ng-repeat="category in vm.categories">
{{category.description}}
</option>
</select>
</div>
</div>
The double curly bracket interpolation directive attempts to convert the category value to a string. This does not work well when the category value is an object.
For more information, see AngularJS ng-value Directive API Reference.
It says [Object Object] when I do <span>{{vm.selectedCategory}}</span> and i can't read the subcategories
Pipe the vm.selectedCategory into the json filter:
<span>{{ vm.selectedCategory | json }}</span>
This allows you to convert a JavaScript object into JSON string.
This filter is mostly useful for debugging. When using the double curly notation the binding is automatically converted to JSON.
For more information, see AngularJS json Filter API Reference.

Vue JS Array Formatting

I am having a terrible time wrapping my head around arrays for Vue JS.
new Vue({
el: '#app',
template: `
<div class="cascading-dropdown">
<div class="dropdown">
<span>Cereal:</span>
<select v-model="cerealname">
<option value="">SELECT A CEREAL</option>
<option v-for="(addon1_obj, addon1) in cereals" :value="addon1">{{addon1}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 1:</span>
<select :disabled="addons1.length == 0" v-model="addon1">
<option value="">Select Addon 1</option>
<option v-for="(addon2_obj, addon2) in addons1">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 2:</span>
<select :disabled="addons2.length == 0" v-model="addon2">
<option value="">Select Addon 2</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 3:</span>
<select :disabled="addons2.length == 0" v-model="addon3">
<option value="">Select Addon 3</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div>Addon 1 text <input id="addon1desc" type="text" v-midel="addon1desc"></div>
<div>Addon 2 text <input id="addon2desc" type="text" v-midel="addon2desc"></div>
<div>Addon 3 text <input id="addon3desc" type="text" v-midel="addon3desc"></div>
</div>
`,
data: function() {
return {
cereals: {
"Lucky Charms": {
"Marshmallows": ["Green Clovers",
"Pink Hearts",
"Yellow Moons",
"Blue Diamonds",
"Purple Horseshoes"
]
},
"Froot Loops": {
"Loops": ["Red Loop",
"Green Loop",
"Blue Loop",
"Yellow Loop"
]
}
},
addons1: [],
addons2: [],
cerealname: "",
addon1: "",
addon2: "",
addon3: ""
}
},
watch: {
cerealname: function() {
// Clear previously selected values
this.addons1 = [];
this.addons2 = [];
this.addon1 = "";
this.addon2 = "";
this.addon3 = "";
// Populate list of countries in the second dropdown
if (this.cerealname.length > 0) {
this.addons1 = this.cereals[this.cerealname]
}
},
addon1: function() {
// Clear previously selected values
this.addons2 = [];
this.addon2 = "";
this.addon3 = "";
// Now we have a continent and country. Populate list of cities in the third dropdown
if (this.addon1.length > 0) {
this.addons2 = this.cereals[this.cerealname][this.addon1]
}
}
}
})
.dropdown {
margin: 10px 0;
padding: 10px 0;
border-bottom: 1px solid #DDD;
}
.dropdown span {
display: inline-block;
width: 80px;
}
<body>
<div id="app"></div>
</body>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
Here's a fiddle: https://jsfiddle.net/4zfh80au/
When you select Lucky Charms in the first dropdown, the 2nd dropdown changes to Marshmallows. The 3rd and 4th dropdowns change to the individual marshmallow types.
What I want is to add data to the text boxes based on the dropdown selection. When you choose the 2nd dropdown to be Marshmallows, I want the corresponding text box to fill in some text like "Marshmallows are yummy." The 3rd and 4th dropdowns should do the same.
Could anyone please point me in the right direction?? Much appreciated.
The binding is already set up in each of your <select> tags with v-model="addon1", addon2, and addon3.
In your <div>, have the bindings in the text fields match the ones defined in your <select> tags like so:
...
<div class="dropdown">
<span>Addon 1:</span>
<select :disabled="addons1.length == 0" v-model="addon1">
<option value="">Select Addon 1</option>
<option v-for="(addon2_obj, addon2) in addons1">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 2:</span>
<select :disabled="addons2.length == 0" v-model="addon2">
<option value="">Select Addon 2</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 3:</span>
<select :disabled="addons2.length == 0" v-model="addon3">
<option value="">Select Addon 3</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div>Addon 1 text <input id="addon1desc" type="text" v-model="addon1"></div>
<div>Addon 2 text <input id="addon2desc" type="text" v-model="addon2"></div>
<div>Addon 3 text <input id="addon3desc" type="text" v-model="addon3"></div>
</div>
...
You can check out the working fiddle here.
Additionally, the fiddle link that you shared had a typo in v-model. If you run the app, you should see an error thrown into the console that says something like this:
[Vue warn]: Failed to resolve directive: midel
(found in anonymous component - use the "name" option for better debugging messages.)
If something in the app is failing, you can use the console to better debug your code.
For anyone wanting the solution, the Fiddle is here:
https://jsfiddle.net/q1jm0ccf/
The array looks as follows:
data: function() {
return {
cereals: {
"Lucky Charms": {
"Marshmallows": [
{"title": "Green Clovers", "value": "The Green Clovers is nice"},
{"title":"Pink Hearts", "value": "The Pink Hearts is cool"},
{"title":"Yellow Moons", "value": "The Yellow Moons is best"},
{"title":"Blue Diamonds","value": "The Blue Diamonds is bad"},
{"title":"Purple Horseshoes","value": "The Purple Horseshoes is normal"},
{"title":"Red","value": "The red ones are great."},
{"title":"Yellow","value": "The yellow ones are better"}
]
},
"Froot Loops": {
"Loops": [
{"title":"Red","value": "Red loops are cherry"},
{"title":"Green","value": "Green loops are lime"}
]
}
},

get dropdown value for SELECT from db instead of hardcoding at UI (Angularjs)

I have the following dropdown select HTML tags
<label>Car:</label>
<select ng-model="params.CarName">
<option value="x" disabled="" selected="">Select Car...</option>
<option value="BMW">BMW</option>
<option value="Audi">Audi</option>
</select>
<label>Model:</label>
<select ng-if="params.CarName == 'BMW'" ng-model="params.CarModel" ng-change="PerfRpt()">
<option value="x" disabled="" selected="">BMW Sports</option>
<option value="BMW 328i">BMW 328i</option>
<option value="BMW Sports">BMW Sports</option>
</select>
<select ng-if="params.CarName == 'Audi'" ng-model="params.CarModel" ng-change="PerfRpt()">
<option value=" " disabled="" selected="">Audi Sports</option>
<option value="Audi i345">Audi i345</option>
<option value="Audi Sports">Audi Sports</option>
</select>
<select ng-if="params.CarName!= 'BMW' && params.CarName != 'Audi'">
<option>-</option>
</select>
As per the above code, this is wat it does, We have a dropdown to select Car. It is either BMW or Audi.
Depending on this selection, The Model dropdown will be shown. For e.g If we select Audi for Car in the first dropdown, then Audi i345 and Audi Sports will be shown as the dropdown in the 'Model' field.
Similarly, other options will be shown if we select BMW.
Now I am planning to put the Car data in DB along with Model data. Once the page loads, the data will be retrieved and displayed to the user for the 'Car' section. Once the car is selected, depending on the value of the car, its corresponding Model will be updated in the Model section then.
I can get the data from backend to frontend. I want to know how to display those values dynamically and not hardcode like I have done here.
I have the following response from database for the Car.
Results: Array[2]
0: Object
Car:BMW
1: Object
Car:Audi
You need an array with the Car Names, eg.
$scope.carNameOptions = [
'BMW'
'Audi'
];
Then you would do this:
<select ng-model="params.CarName">
<option value="">Select car...</option>
<option ng-repeat="car in carNameOptions" value="{{car}}">{{car}}</option>
</select>
I would suggest making params.CarModel an object with the keys being the CarNames, and the value an array with options for the car like above. Then you can do this:
<select ng-model="params.CarModel">
<option value="">Select model...</option>
<option ng-repeat="carModel in carModelOptions[params.CarName]" value="{{carModel}}">{{carModel}}</option>
</select>
eg
$scope.carModelOptions = {
'BMW' : [
'BMW 328i',
...
],
'Audi' : [...]
}
Since you have only the cars models stored in your database, you can retrieve them and then make a new array putting inside it the types, as follows:
(function() {
"use strict";
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope'];
function MainCtrl($scope) {
// From database
var data = [
'BMW',
'Audi'
];
$scope.cars = [
{
"model":"BMW",
"types":[
"BMW 328i",
"BMW Sports"
]
},
{
"model":"Audi",
"types":[
"Audi i345",
"Audi Sports"
]
}
];
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body ng-controller="MainCtrl">
<div class="col-md-12">
<select class="form-control" ng-options="car.model for car in cars" ng-model="carSelected">
<option value="" label="Select a car" hidden></option>
</select>
<select class="form-control" ng-disabled="!carSelected" ng-options="type for type in carSelected.types" ng-model="typeSelected">
<option value="" label="Select a type" hidden></option>
</select>
<hr>
Car selected: <pre ng-bind="carSelected | json"></pre>
Type selected: <pre ng-bind="typeSelected"></pre>
</div>
</body>
</html>
Note: I've used ngOptions directive, which one MUST be used for <select>, not ngRepeat.
I hope it helps.

ui-select selected not working in angularjs

I'm currently using ui-select component in AngularJS.
<ui-select ng-model="tipData.category" search-enabled="false" name="category" required="required" class="orange-site-color-select custom-select">
<ui-select-match><span class="ui-select-result">{{$select.selected}}</span></ui-select-match>
<ui-select-choices repeat="tipcat in tipCategory"><span ng-bind-html="tipcat.name"></span></ui-select-choices>
</ui-select>
I'd like to know how can I implement auto select value displayed in that ui-select as retrieving data (just id) from database and displayed in that ui-select?
try this
<div ng-controller="DemoCtrl" ng-app="main">
<select ng-model="selectedCountry">
<option value="">Select Account</option>
<option ng-repeat="x in chooseCountries" value="{{x.countryId}}" >{{x.name}}</option>
</select>
</div>
And script code
var app = angular.module('main', []);
app.controller('DemoCtrl', function ($scope) {
$scope.chooseCountries=[
{countryId : 1, name : "France - Mainland", desc: "some description" },
{countryId : 2, name : "Gibraltar", desc: "some description"},
{countryId : 3, name : "Malta", desc: "some description"}
];
$scope.selectedCountry = $scope.chooseCountries[1].countryId.toString();
});
You can use selected property.
<div ng-controller="DemoCtrl" ng-app="main">
<select ng-model="selectedCountry">
<option value="">Select Account</option>
<option ng-repeat="x in chooseCountries" value="{{x.countryId}}" selected="{{x.name}}=Malta">{{x.name}}</option>
</select>
</div>

Select option doesnt bind with ng-model

I have an object with AllCountries and SelectedCoutry.
I want to list all countries on a <select> and select the option with ng-model by the value of SelectedCountry.
But the default value of combobox gets selected null.
Example:
angular.module('ngPatternExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.OBJ = {
"OBJ": {
"AllCountries": [
{
"country": "USA",
"id": 1
},
{
"country": "Mexico",
"id": 2
},
{
"country": "Canada",
"id": 3
}
],
"SelectedCountry": {
"country_id": 1,
}
}
}
$scope.AM = $scope.OBJ.OBJ;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="ngPatternExample">
<div ng-controller="ExampleController">
selected country: {{AM.SelectedCountry.country_id}} <br/>
<select class="form-control" ng-model="AM.SelectedCountry.country_id">
<option value=""> --- </option>
<option ng-repeat="co in AM.AllCountries" value="{{co.id}}"> {{co.country}} </option>
</select>
</div>
</body>
(http://plnkr.co/edit/PI3tOrIMSTMwGC0rYA5q?p=preview)
p.s A good explanation why this doesnt work allways in Angular would be great
Replace your select with this
<select class="form-control" ng-options="co.id as co.country for co in OBJ.OBJ.AllCountries" ng-model="AM.SelectedCountry.country_id">
<option value=""> --- </option>
</select>
Use ngOptions directive instead of repeating options in the select box by yourself.
<select
class="form-control"
ng-model="AM.SelectedCountry.country_id"
ng-options="co.id as co.country for co in AM.AllCountries">
<option value=""> --- </option>
</select>
Updated plunk:
http://plnkr.co/edit/FZcJVkJQlbLM3k544s8S?p=preview

Categories

Resources