As a follow up to jQuery Mobile Cascaded Selects using MVC4 and KnockoutJs, I have created this AngularJS Cascaded Selects tutorial that gets it’s data from the Chinook Web API Project. The application uses angular data-binding and the ngOptions attribute to dynamically generate a list of option elements for each of the three HTML select elements.

index.html

<!DOCTYPE HTML> < html ng-app = "ChinookApp" > < head > < script src = "http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js" ></ script > < script src = "libs/angular-resource.min.js" ></ script > < script src = "app.js" type = "text/javascript" ></ script > < link href = "http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css" rel = "stylesheet" > < link rel = "stylesheet" href = "app.css" > < title >Angular Cascading Selects</ title > </ head > < body > < div ng-controller = "MainCtrl" > < h2 >Angular Cascading Selects</ h2 > < div class = "span6 body-content" > < form class = "form-horizontal" > < div class = "control-group" > < label class = "control-label" for = "inputArtist" >Artist</ label > < div class = "controls" > < select id = "inputArtist" ng-model = "artist.selected" ng-options = "artist.ArtistId as artist.Name for artist in artists" ng-change = "artistChanged(artist.selected)" > </ select > </ div > </ div > < div class = "control-group" > < label class = "control-label" for = "inputAlbum" >Album</ label > < div class = "controls" > < select id = "inputAlbum" ng-disabled = "artistNotSelected" ng-model = "album.selected" ng-options = "album.AlbumId as album.Title for album in albums" ng-change = "albumChanged(album.selected)" > </ select > </ div > </ div > < div class = "control-group" > < label class = "control-label" for = "inputTrack" >Track</ label > < div class = "controls" > < select id = "inputTrack" ng-disabled = "albumNotSelected" ng-model = "track.selected" ng-options = "track.TrackId as track.Name for track in tracks" > </ select > </ div > </ div > </ form > </ div > </ div > </ body >

If your connecting to your REST Web API with a URL that has a port number, such as the built-in Visual Studio Development Server, you will need to escape it with two backslashes:

chinookApp.constant('urlBase', 'http://localhost\\:65374/api/');

app.js

'use strict' ; var chinookApp = angular.module( 'ChinookApp' , [ 'ngResource' ]); chinookApp.constant( 'urlBase' , 'https://jimfrenette.com/api/chinook/' ); chinookApp.config([ '$httpProvider' , function ($httpProvider) { $httpProvider.defaults.useXDomain = true ; delete $httpProvider.defaults.headers.common[ 'X-Requested-With' ]; }]); chinookApp.factory( 'ArtistsService' , function ($resource, urlBase){ return $resource(urlBase + 'artists' , {}, { query : {method : 'GET' , isArray : true } }); }); chinookApp.factory( 'AlbumsService' , function ($resource, urlBase){ return $resource(urlBase + 'albums/:artistid' , { artistid : '@artistid' }, { query : {method : 'GET' , isArray : true } }); }); chinookApp.factory( 'TracksService' , function ($resource, urlBase){ return $resource(urlBase + 'tracks/:albumid' , { albumid : '@albumid' }, { query : {method : 'GET' , isArray : true } }); }); chinookApp.controller( 'MainCtrl' , function ($scope, ArtistsService, AlbumsService, TracksService) { $scope.artistNotSelected = true ; $scope.artists = ArtistsService.query({}, function (artists) { artists.unshift({ "ArtistId" : 0 , "Name" : "-- Select Artist --" }); $scope.artist = {selected : artists[ 0 ].ArtistId}; }); $scope.albumNotSelected = true ; $scope.album = {selected : "0" }; $scope.trackNotSelected = true ; $scope.track = {selected : "0" }; $scope.artistChanged = function (selectedArtistId) { $scope.artistNotSelected = ! selectedArtistId; if ($scope.artistNotSelected) { $scope.album = {}; } else { $scope.albums = AlbumsService.query({ artistid : $scope.artist.selected }, function (albums) { albums.unshift({ "AlbumId" : 0 , "Title" : "-- Select Album --" }); $scope.album = {selected : albums[ 0 ].AlbumId}; $scope.albumNotSelected = true ; $scope.tracks = []; }); } }; $scope.albumChanged = function (selectedAlbumId) { $scope.albumNotSelected = ! selectedAlbumId; if ($scope.albumNotSelected) { $scope.track = {}; } else { $scope.tracks = TracksService.query({ albumid : $scope.album.selected }, function (tracks) { tracks.unshift({ "TrackId" : 0 , "Name" : "-- Select Track --" }); $scope.track = {selected : tracks[ 0 ].TrackId}; }); } }; });

Resources