Creating a wrapper directive for Bootstrap-UI's uib-datepicker-popup - angularjs

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.

Related

Extend Angular-UI Datepicker directive

I have been using the ui-datepicker from Angular-ui-bootstrap a lot, but every time I use it, I need to set it up, the same way I do every time.
So I was wondering if I could create a directive called custom-datepicker I could use like
<custom-datapicker>
So that I can set the settings in the directive once, and then reuse it everywhere on my site.
I tried to create a directive like this one below
app.directive('customDatapicker', function () {
return {
restrict: 'E',
require: 'ngModel',
templateUrl: function (elem, attrs) {
return '/AngularJS/Directives/customDatapicker.html'
},
link: function (scope, element, attrs, ngModel) {
$scope.inlineOptions = {
showWeeks: true
};
$scope.dateOptions = {
formatYear: 'yy',
startingDay: 1
};
$scope.open = function () {
$scope.opened = true;
};
$scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate'];
$scope.format = $scope.formats[0];
$scope.selected = {
opened: false
};
},
});
I am also trying to use a template, so that i can do some custom html wrapper around it. So far I have got nothing besides the sample html from
TemplateHTML
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="ngModel" is-open="selected.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" alt-input-formats="altInputFormats" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
In the Template html I'm trying to make the binding between my directive's input model in the datepicker's ng-model by doing ng-mode='ngModel', which i don't think is the right way.
I use my directive in my html page like this, where data is a JS Date Object
<custom-datapicker ng-model='data'>
Can this be done?
Instead of using ng-model on your custom directive, define a custom attribute, then pass that value into the ng-model attribute of the datepicker.
<custom-datepicker customAttrib="val" ...
Then in the template:
<input ng-model="{{customAttrib}}" ...
Interestingly I have been looking at something very similar today. You can pass your model using a two way binding and it mostly works.
<my-datepicker ng-model="myDateModel"></my-datepicker>
Directive:
scope: {
ngModel: '='
}
Template:
<input type="text" ng-model="ngModel" />
The issue is that if you manipulate the model outside of using the datepicker the model classes (ng-valid, ng-touched, etc) on your directive don't update. The classes on the input and the form do...

Input value from a specialized controller isn't being sent back to the main controller

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>

Angular invoke controller fn from directive isolated template

I want on-click event from directive invoke some function from my controller. But for some reason it doesn't work. I want my datepicker to expand when I event is fired. Could you please help me to investigate what is wrong my in my current build?
app.js
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope :{
model:'=model',
minDate:'=minDate',
isOpened:'=isOpened',
openFunction: '&'
},
templateUrl: 'templates/datepicker/datepicker.html',
link: function(scope, elm, attrs) {
}
};
});
app.controller('FlightDatePickerController', function ($scope) {
$scope.openFunction = function($event, isDepart) {
$event.preventDefault();
$event.stopPropagation();
$scope.departureOpened = true;
};
};
datepicker.html
<fieldset>
<pre>{{model}}</pre>
<div class='input-group'>
<input type="text" class="form-control" datepicker-popup ng-model="{{model}}" min-date="{{minDate}}" is-open="{{isOpened}}" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span ng-click='openFunction({event:event}, {isDepart:isDepart})' class="btn btn-default input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</fieldset>
index.html
<div ng-controller="FlightDatePickerController">
<div class="col-md-2">
<my-datepicker model="departureDate" minDate="minDateDeparture" isOpened="departureOpened" open-function="openFunction($event, isDepart)"></my-datepicker>
</div>
</div>
You can add a controller attribute to your directive, in order to bind some function to your template.
In your case, you can do :
Directive
app.directive('myDatepicker', function() {
return {
restrict: 'E',
scope :{
model:'=model',
minDate:'=minDate',
isOpened:'=isOpened'
},
templateUrl: 'templates/datepicker/datepicker.html',
controller: 'FlightDatePickerController'
};
});
Datepicker.html
<div ng-controller="FlightDatePickerController">
<div class="col-md-2">
<my-datepicker model="departureDate" minDate="minDateDeparture" isOpened="departureOpened"></my-datepicker>
</div>
</div>
Your overall implementation is correct, but you made couple of mistakes.
ng-click should be like adding parameter in JSON like structure.
ng-click='openFunction({event:$event, isDepart:isDepart})'
& then your directive element should have
open-function="openFunction($event, isDepart)"

Not getting value of input type created through a directive in angularJS

I am using a directive for datepicker in angularJS. Below is my code for directive. It works well and it shows datepicker when I add datepicker directive in a div.now I want to use the selected date in my form when I send data in controller and from controller I pass it to php api via $http. when I check the data in php it does not shows date. Rest all fields are working fine only the field created with directive is not showing the values. I am pasting the code for directive, controller and form.
app.directive('datepicker', function () {
return {
restrict: 'A',
controller: 'datepickerCtrl',
controllerAs: 'dp',
template: '<div id="dob" ng-controller="profileCtrl" dob class="input-group date" data-date-format="dd-mm-yyyy" ><input class="from_to_input" name="1" type="text" ng-class={inputError:dp.validDate==false} class="form-control input-sm" ng-model="profile.value"/><span class="input-group-addon"><span class="glyphicon glyphicon-th"></span></span></div>',
scope: {
'value': '='
},
link: function (scope, element, attribute) {
// scope.variable = attribute.value;
}
};
});
below is code for controller for datepicker
app.controller('datepickerCtrl', function ($scope) {
$('.date').datepicker({ autoclose: true, todayHighlight: true });
var inputDate = new Date(moment($scope.value));
$scope.value = moment(inputDate).format("DD-MM-YYYY");
$('.date').datepicker('setDate', inputDate);
$scope.$watch('value', function (newVal) {
});
});
below is code for my form page
<form name="profileForm" role="form">
<input type="email" id="" name="email" ng-model="profile.email" class="login_input_area" placeholder="Email">
<div class="col-xs-2" datepicker value="date2"></div>
<input type="submit" value="Submit" ng-click="saveprofile(profile)" class="submit_btn">
</form>
My friend!
Please check online video tutorials for Angular directives!
Anyway, in your directive you have ng-model="profile.value", that should be "value" I think
second: remove ng-controller="profileCtrl" from directive template. Directives has they own local scope.
third: I prefer using restrict: "E", so you can use as custom tag, more readable on my opinion
one more: scope: { value : "="} , not scope: { 'value' : "="}
couple more: remove both controller: 'datepickerCtrl', controllerAs: 'dp'

Angular Bootstrap Datepicker directive to minimize html elements

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>'
};
});

Resources