I've got a form with an address. The first address field I'm collecting is a zip code. After the zip code has been entered, a custom directive queries an external API and fetches an object containing a city, state, county, and a list of all counties in the state.
On the zipcode field, I've got attributes passing in the models assigned to the City, the State and the County input fields. The directive takes these and populates the scope
variable so that the value for each is synchronized.
My problem is on the County select box. I need to first populate the field with a list of all counties in the state (again, all available options are given by the API response). Once the field has been populated, I need to set the value.
Setting is easy. It works. I can't figure out for the life of me how to populate the field with the available options.
- I can't use the element ID, because I need this directive to be reusable for multiple addresses in this form.
- I can't use ng-options, at least not in my view, because I don't have the data to populate until after the zip code has been entered.
Here's a small snippet of my form, showing the zipcode and the county fields:
<div class="form--row">
<div class="form--col5">
<label class="form--label" for="nbZip">Zip</label>
<input
id="nbZip"
class="form--input"
type="text"
ng-model="data.ClientDetails.zip"
ng-required="data.Client == 'true'"
blur="update()"
ziplookup
zip-city="data.ClientDetails.city"
zip-state="data.ClientDetails.state"
zip-county="data.ClientDetails.county"
>
</div>
<!-- First Client NB County -->
<div class="form--row">
<div class="form--col5">
<label class="form--label" for="nbCounty">County</label>
<select class="form--input" id="nbCounty" ng-model="data.ClientDetails.county" ng-required="data.Client == 'true'" change="update()">
<!-- generate county list! -->
</select>
</div>
</div>
And my directive: (ZipCode is a custom service I inject into the directive.)
directives.directive(
'ziplookup',
['ZipCode',
function (ZipCode) {
var scope
, element
, zipData;
var getZipData = function (event) {
// When this function is called, `this` is
// bound to the scope matching the element.
scope = this;
// We also get access to the element itself.
element = $(event.target);
ZipCode
// Shoot off our call for the location data,
// using the value in the `<input>`.
.getLocationData(element.val())
// When data is returned, we will set the
// returned data.
.then(setZipData)
// Update the view with the returned data.
.then(updateCity)
.then(updateState)
.then(updateCounty)
};
var setZipData = function (data) {
// This function makes the scope and data
// accessible from the `update___` functions
// that follow.
// We'll set `zipData` to the returned data.
zipData = data.data;
};
var updateCity = function () {
scope.city = zipData.city;
};
var updateState = function () {
scope.state = zipData.state;
};
var updateCounty = function () {
scope.county = zipData.county;
};
var link = function (scope, elem) {
elem.bind('blur', angular.bind(scope, getZipData));
};
return {
restrict: 'A',
scope: {
city: '=zipCity',
state: '=zipState',
county: '=zipCounty'
},
link: link
};
}]);
I've got a form with an address. The first address field I'm collecting is a zip code. After the zip code has been entered, a custom directive queries an external API and fetches an object containing a city, state, county, and a list of all counties in the state.
On the zipcode field, I've got attributes passing in the models assigned to the City, the State and the County input fields. The directive takes these and populates the scope
variable so that the value for each is synchronized.
My problem is on the County select box. I need to first populate the field with a list of all counties in the state (again, all available options are given by the API response). Once the field has been populated, I need to set the value.
Setting is easy. It works. I can't figure out for the life of me how to populate the field with the available options.
- I can't use the element ID, because I need this directive to be reusable for multiple addresses in this form.
- I can't use ng-options, at least not in my view, because I don't have the data to populate until after the zip code has been entered.
Here's a small snippet of my form, showing the zipcode and the county fields:
<div class="form--row">
<div class="form--col5">
<label class="form--label" for="nbZip">Zip</label>
<input
id="nbZip"
class="form--input"
type="text"
ng-model="data.ClientDetails.zip"
ng-required="data.Client == 'true'"
blur="update()"
ziplookup
zip-city="data.ClientDetails.city"
zip-state="data.ClientDetails.state"
zip-county="data.ClientDetails.county"
>
</div>
<!-- First Client NB County -->
<div class="form--row">
<div class="form--col5">
<label class="form--label" for="nbCounty">County</label>
<select class="form--input" id="nbCounty" ng-model="data.ClientDetails.county" ng-required="data.Client == 'true'" change="update()">
<!-- generate county list! -->
</select>
</div>
</div>
And my directive: (ZipCode is a custom service I inject into the directive.)
directives.directive(
'ziplookup',
['ZipCode',
function (ZipCode) {
var scope
, element
, zipData;
var getZipData = function (event) {
// When this function is called, `this` is
// bound to the scope matching the element.
scope = this;
// We also get access to the element itself.
element = $(event.target);
ZipCode
// Shoot off our call for the location data,
// using the value in the `<input>`.
.getLocationData(element.val())
// When data is returned, we will set the
// returned data.
.then(setZipData)
// Update the view with the returned data.
.then(updateCity)
.then(updateState)
.then(updateCounty)
};
var setZipData = function (data) {
// This function makes the scope and data
// accessible from the `update___` functions
// that follow.
// We'll set `zipData` to the returned data.
zipData = data.data;
};
var updateCity = function () {
scope.city = zipData.city;
};
var updateState = function () {
scope.state = zipData.state;
};
var updateCounty = function () {
scope.county = zipData.county;
};
var link = function (scope, elem) {
elem.bind('blur', angular.bind(scope, getZipData));
};
return {
restrict: 'A',
scope: {
city: '=zipCity',
state: '=zipState',
county: '=zipCounty'
},
link: link
};
}]);
Share
Improve this question
edited Feb 6, 2013 at 22:03
jdp
asked Feb 6, 2013 at 21:48
jdpjdp
3,5162 gold badges32 silver badges55 bronze badges
1 Answer
Reset to default 6You can use the ng-options
even without any data available, you just have to populate the correct model as soon as you receive the API response.
For example, assuming that the API response will populate the $scope.availableCounties
:
$scope.availableCounties = ZipCodeAPI.getCounties(zipCode);
you can then use it to define the ng-options
:
<select class="form--input" id="nbCounty" change="update()"
ng-model="data.ClientDetails.county" ng-required="data.Client == 'true'"
ng-options="county.id as county.name for county in availableCounties">
</select>
Initially it won't show anything because $scope.availableCounties
will be undefined
, but as soon as you populate the model (and if everything is wired up correctly) Angular will automatically add the options to the <select>
field.
Check this example: http://jsfiddle/bmleite/tD9B4/