How to create an AngularJS directive that binds to a variable? - javascript

I want to build a directive for showing datepicker textboxes, i.e regular textboxes which have JQuery UI's datepicker used on them, so when the user clicks them, a datepicker box opens to let them pick the date, etc.
I want to bind this directive somehow to a property on my scope. E.g if it were a normal textbox and I did ng-model='myDate' then $scope.myDate would be updated if the user typed in a new date. In the same way, I want to bind this field from the directive so when the user picks a date, it updates the scope property its bound to.
The problem is, I want to display the directive using something like this:
<datepicker name='something' value='2013-07-20' model='myProperty' />
And have the directive replace it with the <input type="text" /> etc. So I can't use ng-model.
How else can I bind the model property to the directive so that it updates whenever the user changes it?

See if this is what you want:
HTML
<div ng-app="app" ng-controller="Ctrl">
<foo model="property"></foo>
<input type="text" ng-model="property">
</div>
Javascript
angular.module('app', [])
.directive('foo', function() {
return {
restrict: 'E',
replace: true,
scope: { model: '=' },
template: '<input type="text" ng-model="model">'
};
})
.controller('Ctrl', function($scope) {
$scope.property = 'Foobar';
});
jsFiddle
In order to use ng-model instead of model, you'll need to wrap the input in a container tag. Here's another jsFiddle script that illustrates it.
Finally, there's a date picker control in Angular UI Bootstrap. Perhaps it already does what you need.

Related

AngularJS - how to add dynamic ng-model

I'm working with a form and would like to add ng-model on dynamic input elements. I have a scope variable defined as:
$scope.formData = {};
On the page there are a couple of drop-down lists that users can choose an option from and based on those options we are appending some input fields to form body.
formBody.append('<input ng-model="formData.'+obj.Title+'" type="number"></input></br>');
This is not working for me because I'm assuming once the controller runs it can't register any new ng-model. Is there way to add dynamic ng-model or there is a different approach to what I'm trying to do (i.e. build predefined views that can be loaded on the page)?
EDIT:
I have created a jsfiddle that outlines what I'm trying to do - http://jsfiddle.net/k5u64yk1/
If you need to dynamically add html with dynamic bindings that cannot be encapsulated into ng-repeat, ng-if, etc, you have to call $compile on the template once it has been modified to alert AngularJS that it has to reparse the template and initiate a new digest cycle. This will pick up any new ng-model bindings and appropriately tie them to your scope.
HTML:
<div ng-app="MyApp" ng-controller="MyCntrl">
<button ng-click="addInput()">Add Input</button>
<div id="form">
input would go here.
</div>
</div>
JS:
By placing your add input inside of a click event, you avoid an infinite compile loop. Note that this currently resets the state of your form, so if you wanted to work around that you'd need to capture your form state and restore it after compile.
$scope.addInput = function () {
var aForm = (angular.element(document.getElementById('form')));
if ($scope.data["Digital Conversation"][0].MetricType.Title === "Number") {
aForm.append(
'<input ng-model="formData.' +
$scope.data["Digital Conversation"][0].Title.Title +
'" type="number"></input>');
}
$compile(aForm)($scope);
}
You can find the working jsfiddle here: http://jsfiddle.net/k5u64yk1/

AngularJS directive to run after text input changes

I am using a bootstrap calendar datepicker that operates and modifies an <input type="text"> element.
If I use ng-change="myAngularExpression()" it calls the function as soon as the text box is clicked. How can I have the function call after the input actually changes?
In my specific case, a user clicks in the text box that is displaying MM/DD/YYYY and a dropdown calendar comes down, then the angular expression executes, then the user changes the value and nothing happens until they click off the element and back on it.
I'm assuming you're using a jQuery-based Bootstrap datepicker. Unfortunately, mixing jQuery-based Bootstrap widgets with Angular rarely works out well.
The better alternative is to use UI Bootstrap, which is a collection of Bootstrap widgets written entirely in Angular. For example, here's a datepicker.
Once you're using Angular for your widgets (with UI Bootstrap), watching for changes to your date becomes as simple as $scope.$watch():
$scope.$watch('date', function() { /* ... */ });
Full example:
var app = angular.module('app', ['ui.bootstrap']);
app.controller('Main', function($scope) {
$scope.date = new Date();
// watch date for changes
$scope.$watch('date', function(newValue, oldValue) {
if (newValue !== oldValue)
{
$scope.message = 'Date changed from ' + oldValue + ' to ' + newValue;
}
});
});
<div ng-app="app">
<div ng-controller="Main">
<datepicker ng-model="date"></datepicker>
<div ng-bind="message"></div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap-tpls.min.js"></script>
Someone may be able to provide an answer that allows the jQuery-based datepicker to work "good enough" with Angular, but that's a slippery road.
How about ng-keydown ? This way the input will change only when you press your key down on it.
<input ng-keydown='someAwesomeMethodHere()'/>

angularjs scope for ngModel is not working as expected

I have created custom directive drop down in angularjs and ionic-framework. In my dropdown items will open in modal popup which is working very good.
But I have kept search input box with clear button input box which is tide up with ng-model='search' and on button ng-click if I put ng-click="search=''" than it will work very good but if I put function and try to delete from directive module than it will not work
In template
<input type="search" ng-model="search" placeholder="select city...">
<button ng-show="search.length" ng-click="clearSearch()" class="customIcon button button-icon ion-close-circled input-button"></button>
In directive module
$scope.clearSearch = function() {
$scope.search = '';
}
then it will give me error
$scope.search is undefined
I am just very much confuse with $scope thing I also want to know how can i know scope any tool or something
I have also another issue that I kept two custom directive on the same and when first control change its value I want to clear the selection in second directive for that I am doing $watch also but I don't understand How can I do
My plunkr
http://plnkr.co/edit/GxM78QRwSjTrsX1SCxF7?p=preview
I have no experience with Ionic, but these types of issues are usually related to how prototypal inheritance works in JS and AngularJS.
A great explanation on prototypal inheritance in relation to Angular can be found here.
The issue can usually be solved by moving the property into an object:
$scope.viewModel = {};
$scope.clearSearch = function() {
$scope.viewModel.search = '';
};
And then use viewModel.search in your HTML where you need it:
<input type="search" ng-model="viewModel.search" placeholder="select city...">
Demo: http://plnkr.co/edit/4SLfA1CjRWB1XazIhj9d?p=info

angularjs validators not working when used with another directive

I have a "uniqueCheck" directive which checks if the value is already present in a list or not and accordingly validates the ngModel. This directive when used on say an input tag works as expected but when used on a directive which renders an input tag the result is not as expected.
The validator function inside the directive is getting called but it doesn't validate or invalidate the ngModel of the input.
You can view the complete code of the directives on the plnkr link provided
Plnkr Link : plnkr
html is as follows :
<--! when used with a directive -->
<my-wrapper ng-model="values.abc" unique-check="" list="list" prop="name"> </my-wrapper>
<--! when used on an input tag-->
<div ng-form="myform">
<input type="text" unique-check
list="list" prop="name"
name="myfield"
ng-model="values.pqr"/>
<span>isDuplicate:{{myform.myfield.$error.isDuplicate}}</span>
</div>
You're creating 2 separate ngModel instances, that are both updated when the input's changed.
The first is created by the <input> itself, which is the one assigned to 'myform'. This is the one that the <span> error message within my-wrapper is bound too.
The second one is the one created by the my-wrapper directive - which is the one that has the validator attached to it.
If you check the console (for the plnkr below) and inspect the values being output by the validator when the input is changed, you can see that the ngModel associated with the validator, is not the same ngModel that's associated with the form. But that both are actually being updated when the input's changed.
Clear the console once the page has loaded and then check the output when you change the first input.
http://plnkr.co/edit/nz6ODOVpn6lJlb055Svs?p=preview
Why is this happening?
Because both ng-model directives get passed the same string ('values.abc'), which are then evaluated against scope to determine which object property they should watch and update - i.e two way binding.
So when you change the input you're changing the value of scope.values.abc through the inputs ngModel instance. This change is picked up by the my-wrapper ngModelinstance - as it's watching the same object property - that then validates itself.
You can't solve the problem in this way, as the ngModel directive expects a string, not another ngModelinstance.
Solution
You could transfer the attributes from my-wrapper to the input at compile:
app.directive("myWrapper", function(){
var templateFn = function(element, attrs){
return '<div ng-form="myform">'+
'<input type="text" name="myfield"/>'+
'<span>(inside directive) : isDuplicate:{{myform.myfield.$error.isDuplicate}}</span>'
'</div>';
}
return {
restrict :'E',
template : templateFn,
require: 'ngModel',
scope: true,
compile: function(element, attrs) {
var attr;
angular.forEach(element.find('input'), function(elem) {
elem = angular.element(elem)
for(attr in attrs.$attr) {
elem.attr(attrs.$attr[attr], attrs[attr]);
}
});
for(attr in attrs.$attr) {
element.removeAttr(attrs.$attr[attr]);
}
}
}
});
http://plnkr.co/edit/m2TV4BZKuyHz3JuLjHrY?p=preview
Dont use scope in your myWrapper directive, it creates a separate scope of variables. Also, you need to use element.ngModel, not just a string 'ngModel' as the ng-model.
Change your myWrapper directive like this to work:
app.directive("myWrapper", function(){
var templateFn = function(scope, element, attrs){
return '<div ng-form="myform">'+
'<input type="text" name="myfield" ng-model="'+element.ngModel+'"/>'+
'<span>isDuplicate:{{myform.myfield.$error.isDuplicate}}</span>'
'</div>';
}
return {
restrict :'E',
template : templateFn,
//require: 'ngModel',
//scope: {'ngModel' : '='}
}
});

Angularjs input field focus event?

i use angularjs and i have created a normal input field like this:
<input type="text" style="border: none" ng-model="model" >
i want do the following:
if someone clicks in the input field, i want call for example method A. Then he writes text in this field and if the person clicks somewhere in my page so that the input field is no longer focused it should call method B. is this possible with angularjs ? if yes, how can i do that ?
ng-focus is only active at the input event but not at the output..
i want lock something with this, method A sets only a value to true and method B sets the same value to false. but i must know when the input field is active and when not.
You are looking at ng-focus and ng-blur.
<input type="text" style="border: none" ng-model="model" ng-focus="A()" ng-blur="B()">
On a side note, use css classes instead of inline styles.. :)
Or just call the same method with argument and set the value acc:-
<input type="text" style="border: none" ng-model="model" ng-focus="A(true)" ng-blur="A(false)">
If you are using functionality that you may wish to apply to fields throughout your application, you could put the it into a directive.
Here is an example that adds and removes a css class based on the focus or blur of a field:
angular.module('myApp').directive('inputFocus', function () {
var FOCUS_CLASS = 'input-focused';
return {
restrict: 'A',
priority: 1,
require: 'ngModel',
link: function (scope, element, attrs, ctrl) {
element.bind('focus',function () {
element.parent().addClass(FOCUS_CLASS);
}).bind('blur', function () {
element.parent().removeClass(FOCUS_CLASS);
});
}
};
});
You can bind method B to angular's ng-blur directive to detect when an input loses focus
<input type='text' ng-focus='methodA()' ng-blur='methodB()' ng-model='model'>

Categories