How add options to a select with an AngularJS directive? - javascript

I have a select tag (to be used for country selection) which I want to prefill with options using a directive:
<select class="countryselect" required ng-model="cust.country"></select>
My directive goes like
return {
restrict : "C",
link: function postLink(scope, iElement, iAttrs) {
var countries = [
["AND","AD - Andorra","AD"],
["UAE","AE - Vereinigte Arabische Emirate","AE"]
... //loop array and generate opt elements
iElement.context.appendChild(opt);
}
}
I am able to fill the select with additional options, but the ng-model binding does not work. Even if cust.country has a value (e.g. "UAE"), the option is not selected.
How to make the select display the value of cust.country? If think I have some timing problem here.

You can use directive from Angular JS:
Markup:
<div ng-controller="MainCtrl">
<select ng-model="country" ng-options="c.name for c in countries"></select>
{{country}}
</div>
Script:
app.controller('MainCtrl', function($scope) {
$scope.countries = [
{name:'Vereinigte Arabische Emirate', value:'AE'},
{name:'Andorra', value:'AD'},
];
$scope.country = $scope.countries[1];
});
Check the docs of select: Angular Select
EDIT WITH DIRECTIVE
Directive:
app.directive('sel', function () {
return {
template: '<select ng-model="selectedValue" ng-options="c.name for c in countries"></select>',
restrict: 'E',
scope: {
selectedValue: '='
},
link: function (scope, elem, attrs) {
scope.countries = [{
name: 'Vereinigte Arabische Emirate',
value: 'AE'
}, {
name: 'Andorra',
value: 'AD'
}, ];
scope.selectedValue = scope.countries[1];
}
};
});
Main controller:
app.controller('MainCtrl', function($scope) {
$scope.country={};
})
Markup:
<div ng-controller="MainCtrl">
<sel selected-value="country"></sel>
{{country}}
</div>
Working Example: EXAMPLE

You need to add the options to the ngModelController so that this works. E.g. like this:
return {
restrict : 'C',
require: ['select', 'ngModel'],
link: function(scope, element, attrs, controllers) {
var countries = [['A', 'text text']];
countries.forEach(option => {
const optionElement = angular.element('<option/>')
.attr('value', option[0])
.text(option[1]);
element.append(optionElement);
controllers[0].addOption(option.value, optionElement);
});
}
};

Related

Angular floating input label to use typeahead

I have used one of the style from here: http://tympanus.net/Development/TextInputEffects/index.html
To create an input directive, please see plunker: https://plnkr.co/edit/wELJGgUUoiykcp402u1G?p=preview
This working great for standard input fields, however, i am struggling to work wirth Twitter typeahead: https://github.com/twitter/typeahead.js/
Question - How can i use my floating input label with a typeahead?
app.directive('floatInput', function($compile) {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
elemTitle: '=elemTitle',
elemtId: '=elemeId'
},
templateUrl: 'input-template.html',
link: function(scope, elem, attrs) {
var ngModelName = elem.attr('input-model');
var inputElem = angular.element(elem[0].querySelector('input'));
inputElem.attr('ng-model', ngModelName);
$compile(inputElem)(scope);
$compile(inputElem)(scope.$parent);
var inputLabel = angular.element(elem[0].querySelector('label span'));
inputLabel.attr('ng-class', '{\'annimate-input\' : '+ngModelName+'.length > 0}');
$compile(inputLabel)(scope);
},
controller: function($scope) {
$scope.title = $scope.elemTitle;
$scope.inputId = $scope.elemId
}
}
})
HTML:
<div>
<span class="input input--juro">
<input class="input__field input__field--juro" type="text" id="{{inputId}}" ng-model="tmp" />
<label class="input__label input__label--juro" for="{{inputId}}">
<span class="input__label-content input__label-content--juro">{{title}}</span>
</label>
</span>
</div>
The easiest way I know of to achieve this is to initialize the typeahead input in the link function of the directive. For initializing the typeahead with available options I would create an optional parameter to the directive and selectively initialize the input as a typeahead input if the list is provided.
Here is an example of how the directive could look instead:
app.directive('floatInput', function($compile) {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
elemTitle: '=elemTitle',
elemtId: '=elemeId',
typeaheadSrc: '=?typeaheadSrc'
},
templateUrl: 'input-template.html',
link: function(scope, elem, attrs) {
var inputElem = angular.element(elem[0].querySelector('input'));
if(scope.typeaheadSrc && scope.typeaheadSrc.length > 0){
var typeahead = jQuery(inputElem).typeahead({
hint: true,
highlight: true,
minLength: 1
}, {
name: 'typeahead',
source: substringMatcher(scope.typeaheadSrc)
});
}
},
controller: function($scope) {
$scope.title = $scope.elemTitle;
$scope.inputId = $scope.elemId
}
}
});
// from http://twitter.github.io/typeahead.js/examples/
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches= [],
substrRegex = new RegExp(q, 'i');
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push({value: str});
}
});
cb(matches);
};
};
I have updated your plunker to achieve the desired result: Plunker

Angularjs binding array element ng-repeat in directive

I'm trying to display the elements of an array using ng-repeat and a directive. The directive part is important to the solution. However the element of the array is not getting bound and displays an empty value.
The fiddle can be found at http://jsfiddle.net/qrdk9sp5/
HTML
<div ng-app="app" ng-controller="testCtrl">
{{chat.words}}
<test ng-repeat="word in chat.words"></test>
</div>
JS
var app = angular.module('app', []);
app.controller("testCtrl", function($scope) {
$scope.chat = {
words: [
'Anencephalous', 'Borborygm', 'Collywobbles'
]
};
});
app.directive('test', function() {
return {
restrict: 'EA',
scope: {
word: '='
},
template: "<li>{{word}}</li>",
replace: true,
link: function(scope, elm, attrs) {}
}
});
OUTPUT
["Anencephalous","Borborygm","Collywobbles"]
•
•
•
Expected output
["Anencephalous","Borborygm","Collywobbles"]
•Anencephalous
•Borborygm
•Collywobbles
Appreciate your help
You didn't bind word.
You have used isolate scope. If you don't bind with it's scope property,it won't work.
scope: {
word: '='
},
Try like this
<test word="word" ng-repeat="word in chat.words"></test>
DEMO
var app = angular.module('dr', []);
app.controller("testCtrl", function($scope) {
$scope.chat= {words: [
'Anencephalous', 'Borborygm', 'Collywobbles'
]};
});
app.directive('test', function() {
return {
restrict: 'EA',
scope: {
word: '='
},
priority: 1001,
template: "<li>{{word}}</li>",
replace: true,
link: function(scope, elm, attrs) {
}
}
});
Your directive needs to run before ng-repeat by using a higher priority, so when ng-repeat clones the element it is able to pick your modifications.
The section "Reasons behind the compile/link separation" from the Directives user guide have an explanation on how ng-repeat works.
The current ng-repeat priority is 1000, so anything higher than this should do it.

Initialize text input fields in angular directive

I have a directive which shows input fields and I want to initialize those fields with data from the server. The problem is that I can't do that while using ng-model.
Before using a directive I used in the controller something like $scope.field1 = $scope.first.field1
Here's my code. I simplified it for the sake of readability but the idea's here.
In my controller I have this code:
app.controller('MyController',
['$scope', 'myData', function($scope, myData) {
myData.then(function(data) {
$scope.first = data.first;
$scope.second = data.second;
});
}]);
Inside first and second I have 2 field: field1 and field2.
In in html code, I have this bit:
<h1>First</h1>
<my-directive info="first"></my-directive>
<h1>Second</h1>
<my-directive info="second"></my-directive>
The directive is as follows:
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'static/js/myDirective.html',
link: function(scope, element, attrs) {
scope.doStuff = function() {
/* Do stuff with
scope.field1 and scope.field2 */
}
}
};
});
and the myDirective.html code:
<input type="text" ng-model="myfield1" />
<input type="text" ng-model="myfield2" />
<input type="submit" ng-click="doStuff()" />
If in myDirective.html I write:
<input type="text" value="info.field1" />
I can see the value fine.
Any ideas?
probably your directive initializes before your data loads. and your directive sees ng-model as an undefined variable. You are not using info directly in template so no auto $watch's for you :).
you need to $watch your info variable in directive and call your doStuff function on change.
note: i wouldn't recommend adding a controller to a directive just for this task. adding a controller to a directive is needed when you need to communicate with other directives. not for waiting async data
edit
you should do
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'static/js/myDirective.html',
link: function(scope, element, attrs) {
scope.doStuff = function() {
/* Do stuff with
scope.field1 and scope.field2 */
}
scope.$watch('info', function(newValue){
scope.doStuff();
});
}
};
});
Check working demo: JSFiddle.
Define a controller for the directive and do the initialization inside it:
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
info: '='
},
controller: ['$scope', 'myData', function ($scope, myData) {
myData.then(function(data){
$scope.first = data.first;
$scope.second = data.second;
});
}],
...
},
Inside the directive, myfield1 and myfield2 don't exist. You are close to solving the issue by using info.field1 instead.
var myApp = angular.module('myApp', []);
//Faking myData here; in your example this would come from the server
var myData = {
first: {
field1: "FirstField1",
field2: "FirstField2"
},
second: {
field1: "SecondField1",
field2: "SecondField2"
}
};
myApp.controller('MyController', ['$scope', function($scope) {
$scope.first = myData.first;
$scope.second = myData.second;
}]);
myApp.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
info: '='
},
template: '<input type="text" ng-model="info.field1" /><input type="text" ng-model="info.field2" /><input type="submit" ng-click="doStuff()" />',
link: function(scope, element, attrs) {
scope.doStuff = function() {
alert('Info: ' + scope.info.field1 + ', ' + scope.info.field2);
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyController">
<h1>First</h1>
<my-directive info="first"></my-directive>
<h1>Second</h1>
<my-directive info="second"></my-directive>
</div>

Passing data from custom directive into parent controller

I have created custom angular directive. For an example:
Custom directive:
var app=angular.module('app',[]);
app.directive('customDirective',function(){
return{
restrict:A,
controller:customDirectiveController,
scope:{
someArray:"="
}
}
})
Custom directive controller:
app.controller('customDirectiveController',function(scope){
scope.someArray=[];
scope.someArray.push(1);
scope.someArray.push(2);
scope.someArray.push(3);
});
Parent controller:
app.controller('parentCtrl',function($scope){
$scope.result=[];
});
HTML:
<div data-ng-controller="parentCtrl">
<div data-custom-directive="result">
</div>
How can I get value of this someArray from custom directive into Parent controller( result variable should in Parent controller be same as someArray from custom directive controller)?
Here is jsfiddle http://jsfiddle.net/mehmedju/RmDuw/302/
Thanks
You can apply a '$watch' on the array like this:
In the controller:
app.controller('MainCtrl', function($scope) {
$scope.someArray = [];
})
In the HTML:
<div custom-directive arr="someArray">
</div>
In the directive:
app.directive('customDirective', function(){
return {
scope: {
arr: '='
}
}
link: function(scope, element, attrs) {
scope.$watch('arr', function(newVal, oldVal) {
//do your array manipulation here
}
}
})
Alternatively, if you just want to send data back, here's the method:
In the controller, create a function which will accept the value returned from the directive, example:
app.controller('MainCtrl', function(){
$scope.watchVal = function(val) {
//do array manipulation
$scope.apply(); //to update the scope
}
})
In the HTML:
<div custom-directive data-method="watchVal">
</div>
In the directive:
app.directive('customDirective', function(){
return {
scope: {
sendVal: '&method'
},
link: function(scope, element, attrs){
scope.updateVal = function(){
var func = scope.sendVal();
func(scope.someArray);
}
}
}
})
Let's just say you are using tempController
So your code should be
app.controller('tempController', function($scope) {
scope.someArray = []
});
Html Code for this is
<div ng-controller="tempController">
<div custom-directive some-array="someArray">
</div>
You have here a very interesting article about watchers
http://teropa.info/blog/2014/01/26/the-three-watch-depths-of-angularjs.html
You need use a watchCollection
And, If you are playing creating the array and change the reference inside the directive you can lost the reference inside the watcher. This mean don't create o change the array reference inside.
Another interesting way is use ngModel
http://jsfiddle.net/ta66J/
var app = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.formVals = {
dirVals: [
{val: 'one'},
{val: 'two'}
]
};
}
app.directive('dir', function($compile) {
return {
restrict: 'E',
compile: function(element, attrs) {
var html = "<input id='inputId' type='text' ng-model='" + attrs.dirModel + "' />";
element.replaceWith(html);
return function(scope, element, attrs, ngModel) {
$compile(angular.element(element))(scope);
};
},
};
});
The jsfidler is from this interesting thread:
https://groups.google.com/forum/#!topic/angular/QgcRBpjiHAQ
Here is the fixed issue.
<div custom-directive="" data-some-array="result"></div> {{result}}
[http://jsfiddle.net/mehmedju/RmDuw/304/][1]

Sharing Data between two Directives in AngularJS

I have the following code:
<div id='parent'>
<div id='child1'>
<my-select></my-select>
</div>
<div id='child2'>
<my-input></my-input>
</div>
</div>
I also have two directives which get some data from the data factory. I need the two directives to talk to each other such that when a value in select box is changed the input in changes accordingly.
Here's my two directives:
.directive("mySelect", function ($compile) {
return {
restrict: 'E',
scope:'=',
template: " <select id='mapselectdropdown'>\
<option value=map1>map1</option> \
<option value=map2>map2</option> \
</select>'",
link: function (scope, element, attrs) {
scope.selectValue = //dont konw how to get the value of the select
}
};
})
.directive("myInput", function($compile) {
return {
restrict: 'E',
controller: ['$scope', 'dataService', function ($scope, dataService) {
dataService.getLocalData().then(function (data) {
$scope.masterData = data.input;
});
}],
template: "<input id='someInput'></input>",
link: function (scope, element, attrs) {
//here I need to get the select value and assign it to the input
}
};
})
This would essentially do the onchange() function that you can add on selects. any ideas?
You could use $rootScope to broadcast a message that the other controller listens for:
// Broadcast with
$rootScope.$broadcast('inputChange', 'new value');
// Subscribe with
$rootScope.$on('inputChange', function(newValue) { /* do something */ });
Read Angular docs here
Maybe transclude the directives to get access to properties of outer scope where you define the shared variable ?
What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
-> https://docs.angularjs.org/guide/directive
After much research this is what worked...
I added the following:
.directive('onChange', function() {
return {
restrict: 'A',
scope:{'onChange':'=' },
link: function(scope, elm, attrs) {
scope.$watch('onChange', function(nVal) { elm.val(nVal); });
elm.bind('blur', function() {
var currentValue = elm.val();
if( scope.onChange !== currentValue ) {
scope.$apply(function() {
scope.onChange = currentValue;
});
}
});
}
};
})
Then on the element's link function I added:
link: function (scope, elm, attrs) {
scope.$watch('onChange', function (nVal) {
elm.val(nVal);
});
}
Last added the attribute that the values would get set to in the scope:
<select name="map-select2" on-change="mapId" >

Categories

Resources