We are using Angular UI bootstrap implementation for date picker in our project. I need to write many HTML element for date picker, would it be a good idea to write a directive for such tasks? If yes please help me to provide sample code for any such directive.
<p class="input-group">
<input type="text" class="form-control" datepicker-popup ng-model="leaveSearch.td" is-open="$parent.dtLeaveToOpen" ng-required="true" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="datePickerOpen($event,'dtLeaveTo','dtLeaveToOpen')"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
How can wrap all this into single html element, I think using Angular directive we can achieve this. I am looking for similar idea.
Finally I wrote below directive which worked for me:
angularStartDirectives.directive('espireDate', function () {
return {
restrict: 'E',
scope: {
show: '=',
ngModel: '=',
ngClass: '=',
ngRequired:'='
},
replace: true, // Replace with the template below
transclude: true, // we want to insert custom content inside the directive
require: 'ngModel',
link: function (scope, element, attrs) {
scope.open = false;
scope.dpOpen = function () {
scope.open = true;
};
},
template: '<div><div class="input-group input-group-lg" ng-click="dpOpen()" >'
+ ' <input id="attrs.id" readonly="true" type="text" class="form-control" data-is-open="open" datepicker-popup data-ng-model="ngModel" ng-class="ngClass" ng-required="ngRequired" />'
+ ' <span class="input-group-addon">'
+ '<span class="glyphicon glyphicon-calendar"></span>'
+ '</span></div></div>'
};
});
Related
I'm trying to create a wrapper AngularJS directive for Bootstrap-UI's uib-datepicker-popup so I don't have to recreate a bunch of boilerplate each time I need to select a date. I've been working off an example I found here which was written for an earlier version of Angular, and am running into some oddities getting this working.
I've gotten the directive to a point where it displays a popup, however the two-way data binding seems to be broken; the date value in the field's model doesn't propagate into the directive, and when you click on the popup and select a date, it doesn't propagate back out. Does anyone have any ideas on what's going on here?
I've created a Plunker demonstrating the issue here.
Directive code:
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope: {
model: "=",
myid: "#"
},
templateUrl: 'datepicker.html',
require: 'ngModel',
link: function(scope, element) {
scope.popupOpen = false;
scope.openPopup = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.popupOpen = true;
};
scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.opened = true;
};
}
};
});
Template code:
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" id="{{myid}}" uib-datepicker-popup ng-model="model" is-open="opened" ng-required="true" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
You're using ng-model in the directive code, but you're looking for model in the directive template.
<my-datepicker ng-model="selected" myid="someid"></my-datepicker>
And here you are looking for an attribute called model:
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope: {
//this line should be ngModel
model: "=",
myid: "#"
},
Here's a working plunker: https://plnkr.co/edit/s5CT4xGqXtUxgCH8vw8q?p=preview
In general, I try to avoid using names like "model" and "ng-model" on custom directives, as built-in angular attributes should be distinguished from custom attributes.
I simply want to add the class disable when form.$valid is false. The first submit button (not directive) obiouvsly works, but I don't know how to make visible the form state inside the directive. I cannot hardcode the form name in directive, it should be reusable in several forms..
<form id="tA" name="form" ng-controller="ctrl" ng-submit="form.$valid && save()" novalidate>
<input type="text" class="form-control" placeholder="title" name="name" ng-model="model.name" required ng-required="true">
<button class="btn btn-default" ng-class="{'disabled' : !form.$valid}" class="btn btn-default">Submit</button>
<button-raised disabled="!form.$valid">directive Submit</button-raised>
app.directive('buttonRaised', function() {
return {
restrict: 'E',
template: '<button class="mdl-button" ng-class="ngClass" ng-transclude></button>',
scope: {
ngModel: '='
},
transclude: true,
link: function($scope, el, $attrs,formCtrl) {
console.log(formCtrl)
$scope.ngClass = {
// 'disabled': $scope.$parent.formCtrl.$valid,
};
}
};
});
one way to do this is to pass your boolean as a parameter of your directive :
app.directive('buttonRaised', function() {
return {
restrict: 'E',
template: '<button class="mdl-button" ng-disabled="disabled" ng-class="ngClass" ng-transclude></button>',
scope: {
ngModel: '=',
disabled: '='
},
transclude: true,
link: function($scope, el, $attrs,formCtrl) {
console.log(formCtrl)
$scope.ngClass = {
// 'disabled': $scope.$parent.formCtrl.$valid,
};
}
};
});
You can change the directive and html as per this working this
The directive
app.directive('buttonRaised', function() {
return {
restrict: 'E',
template: '<button class="mdl-button" ng-class="{\'disabled\': disableButton}" ng-transclude></button>',
scope: {
disableButton: '='
},
transclude: true,
link: function($scope, el, $attrs,formCtrl) {
console.log(formCtrl)
}
};
});
The html
<form id="tA" name="form" ng-controller="ctrl" ng-submit="form.$valid && save()" novalidate>
<input type="text" class="form-control" placeholder="title" name="name" ng-model="model.name" required ng-required="true">
<button class="btn btn-default" ng-class="{'disabled' : !form.$valid}" class="btn btn-default">Submit</button>
// directive taking parameter disable-button
<button-raised disable-button="!form.$valid">directive Submit</button-raised>
I'm building an application that's making use of Angular UI Bootstrap's datepicker in several spots. I've included some other enhancements to this, such as validation styling, and wanted to propagate it everywhere so I put it in a directive.
app.controller('DateController', function () {
var vm = this;
vm.status = {
opened: false
};
vm.open = open;
function open($event) {
vm.status.opened = true;
}
});
app.directive('customDatePicker', function () {
return {
restrict: 'E',
scope: {
name: '=',
bindingProperty: '=',
minDate: '=',
maxDate: '=',
maxMode: '=',
format: '=',
isRequired: '=',
form: '='
},
templateUrl: "/Scripts/SharedAngular/Templates/datePicker.html"
};
});
With the template URL code looking like:
<div ng-controller="DateController as datePicker">
<p class="input-group">
<input type="text" id="{{ name }}_datePicker" name="{{ name }}_datePicker" class="form-control" max-mode="maxMode" datepicker-popup="{{format}}" ng-model="bindingProperty" min-date="minDate" max-date="maxDate" ng-required="isRequired" close-text="Close" is-open="datePicker.status.opened" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="datePicker.open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
<span ng-show="form.{{ name }}_datePicker.$invalid && form.{{ name }}_datePicker.$dirty" class="glyphicon glyphicon-exclamation-sign form-control-feedback datepicker-error-icon"></span>
</p>
</div>
I'm including this directive in a couple places. It is placed on a page within the scope of a different controller like this:
<div ng-controller="myController as ctrl">
<custom-date-picker name="'somePrefix'" form="myForm" binding-property="ctrl.date" max-mode="'month'" max-date="ctrl.currentDate" format="'yyyy/MM/dd'" is-required="true"></custom-date-picker>
</div>
So, I want to take a property from myController and two-way bind it to the directive's date picker. However, what ends up actually happening is that the current value for ctrl.date property is passed to the datepicker, but any changes to that value that happen in the directive are not sent back. So, it seems that myController and DateController only pass the value one-way, and only when the directive is loaded onto the page. To be clear, the directive doesn't seem to be the source of my issue, since putting the HTML code from the template directly onto my page results in the same behavior. So, it appears that the issue is specifically due to the use of two different controllers.
Previously, I was using ng-model both in the template code and the directive on my primary page. Neither option seems to work. I'm open to any ideas on how to get the input from my directive to be sent back to the myController.
I figured out how to ditch the DateController entirely. I put my expressions directly into the template elements via ng-click and ng-init. The input creates a property called opened and uses ng-init initializes it to false. It then uses that property for the is-open attribute of the datepicker. Then the button just uses ng-click="opened=true". Definitely simplifies things and removes the nesting of controllers.
Directive:
app.directive('customDatePicker', function () {
return {
restrict: 'E',
scope: {
name: '=',
bindingProperty: '=ngModel',
minDate: '=',
maxDate: '=',
maxMode: '=',
format: '=',
isRequired: '=',
form: '='
},
require: 'ngModel',
templateUrl: "/Scripts/SharedAngular/Templates/datePicker.html"
};
});
Template:
<p class="input-group">
<input type="text" id="{{ name }}_datePicker" name="{{ name }}_datePicker" class="form-control" max-mode="maxMode" datepicker-popup="{{format}}" ng-model="bindingProperty" min-date="minDate" max-date="maxDate" ng-required="isRequired" close-text="Close" is-open="opened" ng-init="opened=false" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="opened=true"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
<span ng-show="form.{{ name }}_datePicker.$invalid && form.{{ name }}_datePicker.$dirty" class="glyphicon glyphicon-exclamation-sign form-control-feedback datepicker-error-icon"></span>
</p>
Using the directive:
<div ng-controller="MyController as ctrl">
<custom-date-picker name="'somePrefix'" form="myForm" ng-model="ctrl.Date" max-mode="'month'" max-date="ctrl.currentDate" format="'yyyy/MM/dd'" is-required="true"></custom-date-picker>
</div>
I have custom directive for datepicker. I want to reuse it in several different places. But in order to reuse current directive I have to dynamically pass and change different attributes into my-datepicker directive.
If you look inside datepicker.html I am using following attributes: ng-model="departureDate" min-date="minDateDeparture" is-open="departureOpened".
Question: How do I set this attributes on the my-datepicker element level and pass all the way down to my directive html template? I want to achieve something like that:
<my-datepicker ng-model="departureDate1" min-date="minDateDeparture1" is-open="departureOpened1"></my-datepicker>
<my-datepicker ng-model="departureDate2" min-date="minDateDeparture2" is-open="departureOpened2"></my-datepicker>
Thanks for any help!
datepicker-contoller.js
app.directive('myDatepicker', function() {
return {
restrict: 'E',
templateUrl: 'templates/datepicker/datepicker.html'
};
});
datepicker.html
<fieldset>
<div class='input-group'>
<input type="text" class="form-control" datepicker-popup ng-model="departureDate" min-date="minDateDeparture" is-open="departureOpened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span ng-click="open1($event)" class="btn btn-default input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</fieldset>
Datepicker usage
<div ng-controller="MyController">
<div class="col-md-2">
<my-datepicker></my-datepicker>
</div>
<div class="col-md-2">
<my-datepicker></my-datepicker>
</div>
</div>
Update: See this jsFiddle: http://jsfiddle.net/j31ky7c2/
You can pass the data as well as functions as attribute in your directive.
<my-datepicker min-date="minDateDeparture2" is-open="departureOpened2" some-function="testFunction()"></my-datepicker>
You can receive this data in your directive's scope.
directive('myDatepicker', [function() {
return {
restrict: 'E',
scope: {
minDate: '#',
isOpen: '#',
someFunction: '&'
},
link: function(scope, elm, attrs) {
}
}
}]);
Then you can simply use minDate and isOpen and someFunction in your directive template like:
<div ng-bind="{{::minDate}}"></div>
<div ng-bind="{{::isOpen}}"></div>
<Button ng-click="someFunction()">Click me</Button>
You can see the working example here - or should I say, non-working example.
Basically, I'm trying to make a custom directive called yesno-group. It is a shortcut to do this over and over:
<div class="btn-group">
<button type="button" class="btn btn-default" ng-model="checkboxField" btn-radio="true">Yes</button>
<button type="button" class="btn btn-default" ng-model="checkboxField" btn-radio="false">No</button>
</div>
And here is my yesno-group directive:
myApp.directive('yesnoGroup', function () {
return {
restrict: 'A',
scope: {
field: '=',
buttonClass: '#'
},
replace: true,
template: '<div class="btn-group">' +
' <button type="button" class="{{buttonClass}}" ng-model="field" btn-radio="true">Yes</button>' +
' <button type="button" class="{{buttonClass}}" ng-model="field" btn-radio="false">No</button>' +
'</div>'
};
});
The problem is, yesno-group does not show the value on the initial load. But once you start changing the value, it gets synchronized with the ngModel field.
What am I missing?
Also, how can I make the yesno-group to accept ng-model and use it instead of field? I got that from JsFiddle-Examples - currency input, but wish to use ng-model unless it is a big hassle.
Thank you!
You need to use ng-class, not class.
http://jsfiddle.net/KT6Zd/12/
myApp.directive('yesnoGroup', function () {
return {
restrict: 'A',
scope: {
field: '=',
buttonClass: '#'
},
replace: true,
template: '<div class="btn-group">' +
' <button type="button" ng-class="buttonClass" ng-model="field" btn-radio="true">Yes</button>' +
' <button type="button" ng-class="buttonClass" ng-model="field" btn-radio="false">No</button>' +
'</div>'
};
});