Custom directive to populate cascading drop down - angularjs

I want to populate the select dropdown from database. Right now the data is coming from scope array.
html:
<div ng-controller="DropDownController">
Country:
<select id="country" class="input-sm" name ="country" ng-model="states" ng-options="country for (country, states) in countries" drop-down required>
<option value=''>Select</option>
</select>
States: <select id="state" class="input-sm" name="state" ng-disabled="!states" ng-model="cities" ng-options="state for (state,city) in states" required>
<option value=''>Select</option></select>
City: <select id="city" class="input-sm" name="city" ng-disabled="!cities || !states" ng-model="city" required>
<option value=''>Select</option>
<option ng-repeat="city in cities" value='{{city}}'>{{city}}</option></select>
</div>
Directive:
app.directive('DropDown', function ($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
$http.get('DropDown.do').success(function (data) {
if (data) {
}
});
}
};
});
I am not sure above directive is the right way of approach for my requirement. The servlet or url is not being called when I click on the drop down option. How do I achieve the same? I am still a beginner in angular.

Directives in Angular are used when you want to more easily interact with the DOM, ajax calls have no business there. You should rather use a service or a factory to process the asynchronous request and then simply return the result in the controller for further manipulations. A promise will also be needed since we are dealing with asynchronous jobs.
'use strict';
var app = angular.module('app', []);
app.factory('countryFactory', function($http) {
var getCountries = function() {
return $http.get('ajax.php').success(function(data) {
return data;
})
}
return { getCountries: getCountries }
})
app.controller('DropDownController', function($scope, countryFactory) {
var ajaxPromise = countryFactory.getCountries();
// Promises are executed once $http is done with the asynchronous job
ajaxPromise.then(function(result) {
$scope.countries = result.data;
})
})
The server side (ajax.php) is simply returning an array, here you should replace it with whatever information from the database you are needed
<?php
echo json_encode(array(
array('id' => 1, 'name' => 'USA'),
array('id' => 2, 'name' => 'Australia'),
));
Instead of using a directive, for select and respectively option elements we can use ng-options so the view would look like this:
<div ng-controller="DropDownController">
<select ng-options="country.name for country in countries track by country.id" ng-model="selected">
</div>

check this snippet you had error in directive declaration its camel case , so it should be dropDown not DropDown!
var app = angular.module('app', []);
app.directive('dropDown', function ($http) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
//replace with your call
//$http.get('DropDown.do').success(function (data) {
//// if (data) {
//scope.countries = data;
//}
//});
scope.countries =
{
'USA':
{
'Alabama': ['Montgomery', 'Birmingham'],
'California': ['Sacramento', 'Fremont'],
'Illinois': ['Springfield', 'Chicago']
},
'Australia':
{
'New South Wales': ['Sydney'],
'Victoria': ['Melbourne']
}
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
Selected country : {{states}}
<select id="country" class="input-sm" name="country" ng-model="states" ng-options="country for (country, states) in countries" drop-down required>
<option value=''>Select Country</option>
</select>
</body>

Related

How to add conditional add html attribute?

How to add conditionally attribute in angularjs?
For example I only want to set the multiple attribute on a <select> if my component has a binding set to true. This means if the binding is not given the multiple attribute should not exist on my <select>.
The only solution I found was with ng-if.
You can achieve this by implementing a directive (aka ng-multiple) to handle the property multiple of the select element. The following snippet implements this solution. However, you may want to control the state of your model inside this directive, once the multiple prop will produce an array of selected values, the non-multiple will produce a single object, so this may cause an issue when switching between multiple and non-multiple.
angular.module('myApp', [])
.directive('ngMultiple', function () {
return {
require: ['select', '?ngModel'],
restrict: 'A',
scope: {
multiple: '=ngMultiple'
},
link: function (scope, element, attrs, ctrls) {
element.prop('multiple', scope.multiple);
scope.$watch('multiple', function (multiple) {
if(element.prop('multiple') != multiple){
// may be would be convenient change the ngModel
// to [] or {} depending on the scenario
element.prop('multiple', multiple);
}
});
}
};
})
.controller('myController', function ($scope) {
$scope.condition = true;
$scope.myOptions = [];
});
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-controller="myController">
<label>
<input type="checkbox" ng-model="condition" /> multiple?
</label>
<br>
<select ng-multiple="condition" ng-model="myOptions">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
<option>Option 4</option>
<option>Option 5</option>
</select>
<br>
<tt>myOptions: {{ myOptions }}</tt>
</div>
if boolean condition is true then multiple, else not
<select ng-if="condition" ng-model="some.model" multiple></select>
<select ng-if="!condition" ng-model="some.model"></select>
<select ng-show="condition" ng-model="some.model" multiple></select>
<select ng-hide="condition" ng-model="some.model"></select>
controller,
$scope.condition = true

Calling custom directive form controller

I have written a custom directive to load 3 drop downs and handle the change event of the drop down inside the controller. On change of the dropdown value I am getting the changed value inside my controller function. I need to call my custom directive and pass the new value.
I am new to AngularJs custom directive. Please let me know how to invoke a directive from within the controller.
The view where I render the custom directive:
<div class="container">
<div class="main-header clearfix">
<div class="page-title">
<h3 class="no-margin">Search for a Host</h3>
</div>
<!-- /page-title -->
</div>
<div class="padding-md">
<div class="hostsFilters row">
<select ng-model="BU" ng-change="changeTheView()" class="form- control">
<option value="">BU</option>
<option value="1">BU1</option>
<option value="2">BU2</option>
<option value="3">BU3</option>
<option value="4">BU4</option>
</select>
<span>OR</span>
<select ng-model="Application" ng-change="changeTheView()" class="form-control">
<option value="">Application</option>
<option value="1">App1</option>
<option value="2">App2</option>
<option value="3">App3</option>
<option value="4">App4</option>
</select>
<span>OR</span>
<input type="text" class="form-text" ng-model="hostName.host_name" placeholder="Hostname">
</div>
</div>
<div class="row">
<i-data-grid></i-data-grid>
</div>
The code of my controller:
'use strict';
angular.module('angularFullstackApp')
.controller('MainCtrl', function ($scope) {
$scope.changeTheView=function(){
console.log('Came inside the Change the view function..');
//Now invoke the custom directive.
}
});
The code for my directive:
'use strict';
angular.module('angularFullstackApp')
.directive('iDataGrid', function () {
return {
templateUrl: 'app/iDataGrid/iDataGrid.html',
restrict: 'E',
link: function (scope, element, attrs) {
$(document).ready(function() {
$('#example').dataTable();
} );
}
};
});
You could take a use of attribute, & pass the values inside attribute for which you wanted to place a watch that would fire when any of the value changed
Markup
<i-data-grid watch-values="['BU', 'Application']"></i-data-grid>
Code
'use strict';
angular.module('angularFullstackApp')
.directive('iDataGrid', function () {
return {
templateUrl: 'app/iDataGrid/iDataGrid.html',
restrict: 'E',
link: function (scope, element, attrs) {
$(document).ready(function() {
$('#example').dataTable();
scope.$watch(attrs.watchValues,function(newVal, oldVal){
//newly changed value available here with same sequence
//as you passed ['BU', 'Application'] here, it fires fn when any of value changed,
//newVal[0] contains newly changed value of BU
//& newVal[1] will contain newly changed value of Application
//call what ever code on basis of this values
},true);
});
}
};
});

Can't access options for ng-options inside an ng-repeat

I would like to be able to access a set of select options within an ng-options inside an ng-repeat, but the ng-repeat creates a new scope where I can no longer access this. Is there a way around this without adding it to the repeating data?
<body ng-controller="mainCtrl" class="container">
<test repeater="repeater"></test>
</body>
Template:
<div ng-repeat="repeat in repeater">
<span>{{repeat.type}}</span>
<select ng-options="value for value in options">
<option value="">Choose one:</option>
</select>
</div>
JS:
angular.module('app').controller('mainCtrl', function($scope) {
$scope.repeater = [
{
type: 'best'
},
{
type: 'worst'
},
{
type: 'ok'
}
];
});
angular.module('app').directive('test', function() {
return {
scope: {
repeater: '='
},
templateUrl: 'test.html',
controller: function($scope) {
$scope.options = ['Nope', 'Yup', 'Sure'];
}
};
});
Plunker
You're close, but missing 1 thing. ng-model
<select ng-model="selectedOption" ng-options="value for value in options">
<option value="">Choose one:</option>
</select>

get selected tags text with select2 AngularJS

How do I get user selected multiple tags with angularjs, taking in consideration the following snippet
Markup
<input type="hidden" ui-select2="select2Options" ng-model="list_of_string" style="width:100%" />
<small>hint: start to type with a</small>
Angular scope
$scope.list_of_string = [];
$scope.select2Options = {
data: function() {
api.categories().then(function(response) {
$scope.data = response;
});
return {'results': $scope.data};
},
'multiple': true,
formatResult: function(data) {
return data.text;
},
formatSelection: function(data) {
return data.text;
}
}
Try changing your <input .../> to <select> </select>
like
<select multiple ui-select2 ng-model="list_of_string" style="width: 100%">
<option ng-repeat="option in data">{{option.text}}</option>
</select>

AngularJS Validation

Hi I want to create a custom directive to do form validation. I have a form with checkboxes and some text fields. I want to make sure the user doesn't leave any of the fields empty.
When the user leaves a form empty after pressing submit, I want the directive to highlight the border of the field red. My problem is that when I make an isolate scope directive it doesn't work. When it isn't an isolate scope, all the fields turn red when only one is empty. How can I fix this?
directive.js:
directive('createprofileformerrormsg', function() {
return {
scope:{createprofileformerrormsg:'#'},
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ngModel) {
scope.$watch('formErrors', function() {
if (attr.createprofileformerrormsg == 1) {
elem.css("border", "red solid 1px");
}
});
}
}
});
form.html
<form data-ng-submit="createProfile()">
Ethnicity: <select createprofileformerrormsg="{{formErrors.ethnicity}}" data-ng-
model="ethnicity" >
<option value="Asian">Asian</option>
<option value="Black">Black</option>
<option value="Caucasian">Caucasian</option>
<option value="Hispanic">Hispanic</option>
<option value="Middle Eastern">Middle Eastern</option>
<option value="Native American">Native American</option>
<option value="Other ethnicities">Other ethnicities</option>
</select><br/>
Gender: <select createprofileformerrormsg="formErrors.ethnicity" data-ng-
model="gender">
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
</form>
Controller.js
$scope.createProfile = function() {
if ($scope.ethnicity == null) {
$scope.formErrors.ethnicity = 1;
error_count++;
}
if ($scope.gender == null) {
$scope.formErrors.ethnicity = 1;
error_count++;
}
}
Try this:
Javascript
app.controller('MainCtrl', function($scope) {
$scope.submit = function() {
$scope.$broadcast('submit');
}
})
.directive('highlightOnError', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
scope.$on('submit', function() {
var border = '';
if (ngModel.$invalid)
border = 'red solid 1px';
element.css('border', border);
});
}
};
});
HTML
<body ng-controller="MainCtrl">
<form ng-submit="submit()" novalidate>
<input type="text" ng-model="foo" required highlight-on-error />
<select ng-model="bar" ng-options="option for option in [1, 2, 3]"
required highlight-on-error>
<option value="">choose a number</option>
</select>
<button type="submit">Submit</button>
</form>
</body>
Working Plunker here.

Resources