bootstrap-datepicker (range) - AngularJS Directive - javascript

I'm trying to make use of the bootstrap-datepicker.
I have an existing AngularJS directive, but when setting the initial value, it does not update when making use of a date range.
HTML
<div class="input-group input-daterange" id="fromToDate" calendar ng-model="vm.fromToDate">
<input type="text" class="form-control input-sm" required ng-model="vm.bookingFromDate">
<span class="input-group-addon">to</span>
<input type="text" class="form-control input-sm" required ng-model="vm.bookingToDate">
</div>
Directive
// this directive updates the value, once it has been selected, but not when the initial value has been set**
function calendar() {
return {
require: 'ngModel',
link: function($scope, el, attr, ngModel) {
$(el)
.datepicker({
autoclose: true,
todayHighlight: true,
todayBtn: 'linked',
onSelect: function(dateText) {
$scope.$apply(function() {
ngModel.$setViewValue(dateText);
});
}
});
}
};
};
Then, I tried the following directive (found here), but this doesn't work either for a date range - instead:
function calendar() {
return {
require: '?ngModel',
restrict: 'A',
link: function ($scope, element, attrs, controller) {
var updateModel, onblur;
if (controller != null) {
updateModel = function (event) {
element.datepicker('hide');
element.blur();
};
onblur = function () {
var date = element.val();
return $scope.$apply(function () {
return controller.$setViewValue(date);
});
};
controller.$render = function() {
var date = controller.$viewValue;
element.datepicker().data().datepicker.date = date.from.toDate();
element.datepicker('setValue');
element.datepicker('update');
return controller.$viewValue;
};
}
return attrs.$observe('bdatepicker', function (value) {
var options = {
format: "yyyy/mm/dd",
todayBtn: "linked",
autoclose: true,
todayHighlight: true
};
return element.datepicker(options).on('changeDate', updateModel).on('blur', onblur);
});
}
};
};
Any assistance would be appreciated!
Thanks!
[Update]
CodePen to illustrate the issue:
<p data-height="322" data-theme-id="dark" data-slug-hash="BLkagb" data-default-tab="js,result" data-user="Programm3r" data-embed-version="2" class="codepen">See the Pen Bootstrap-Datepicker (Range) AngularJS by Richard (#Programm3r) on CodePen.</p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>

You could use the following library to solve the issue. datepicker
Edit: to resolve disappearance of date.
<input type="text" class="form-control input-sm" ng-model="vm.bookingFromDate" id="fromDate">
<span class="input-group-addon">to </span>
<input type="text" class="form-control input-sm" ng-model="vm.bookingToDate" id="toDate">
in controller
$('#fromDate').val(vm.bookingFromDate);
$('#toDate').val(vm.bookingToDate);

Related

ng-change update model with latency

everyone.
I have a trouble with angularjs. I created custom directive for input[type="text"] and passed into variable as model. But ng-change event called function with previous value of variable.
Example:
State: 0, Type 1, In function - 0.
State: 1, Type 548, In function - 1.
State:548, Type 3, In function 548.
My html:
<div ng-controller="simpleCTRL">
<mr-textfield is-required="true" value="val" title="Minutes" type="text" change="ChangeVal()"></mr-textfield>
<input type="text" ng-model="val" ng-change="ChangeVal()"/>
</div>
</div>
And js:
<!-- language: lang-js -->
angular.module('SimpleInterestApp', [])
.controller('simpleCTRL', function($scope) {
$scope.val = 0;
$scope.ChangeVal = function() {
console.log($scope.val);
};
})
.directive('mrTextfield', function () {
return {
restrict: 'E',
template: "<div class='textfield'><input type='{{::type}}' ng-required='isRequired' ng-model='value' ng-change='change()'><span class='bar'></span><label>{{::title}}</label></div>",
replace: true,
transclude: true,
scope: {
value:"=",
title:"#",
type:"#",
isRequired:"=",
change:"&"
}
};
});
Wrap console.log inside a $timeout.
$scope.ChangeVal = function() {
$timeout(function() {
console.log($scope.val);
})
};
See working plunker.

How to set datepicker minDate?

I have a directive date-picker.js and view as selectDate.html. I want to set minDate for the date picker when the value of another datepicker changes. How to achieve that?
.directive('selectDate', ['moment', function(moment) {
return {
restrict: 'E',
require:'^ngModel',
templateUrl: 'views/selectDate.html',
replace: true,
scope: {
ngModel: '='
},
link: function($scope, element, attrs) {
$scope.date = $scope.ngModel;
$scope.dateOptions = {
startingDay: 1,
showWeeks: false
};
$scope.dateStatus = {
opened: false
};
$scope.openDatePopup = function() {
$scope.dateStatus.opened = true;
};
$scope.$watch('date', function (newValue, oldValue) {
if (newValue !== oldValue) {
var date = moment(newValue);
$scope.ngModel = date.format('YYYY-MM-DD');
}
});
}
};
selectDate.html
<span class="select-date">
<input type="text" readonly="readonly" datepicker-popup="dd.MM.yyyy" datepicker-options="{startingDay: 1, showWeeks: true}" ng-model="date" show-button-bar="false" current-text="Heute" close-text="Schließen" is- open="dateStatus.opened" min-date="'2014-01-01'" class="form-control" required="required" ng-click="openDatePopup()">
</span>
I am using it like below:
From <select-date ng-model="fromDate"></select-date>
To <select-date ng-model="toDate"></select-date>
I want to set minDate of toDate to fromDate value.
Well you can user ui-bootstrap directive which is lot easier,below is the code
$scope.dateOptions = {
formatYear: 'yyyy',
startingDay: 1
};
$scope.opened = true;
$scope.minDate= new Date();
var maxDate = new Date();
maxDate.setFullYear (maxDate.getFullYear() + 2);//enable for 2 future years..
$scope.maxDate= maxDate;
Your HTML will look like this
<input type="text" class="form-control selection-box"
id="datepicker"
datepicker-popup="{{dateFormat}}"
ng-model="selectedDate" is-open="opened"
ng-class="{'date-changed':colorChange[$parent.$parent.$index +'-'+ $parent.$index]}"
min-date="minDate" max-date="maxDate"
datepicker-options="dateOptions" readonly="readonly"
ng-change="changeDate()"
close-text="Close" ng-required="true" />
To answer OP's question you can have following code changes.
From <select-date ng-model="fromDate" min-date="fromMinDate"></select-date>
To <select-date ng-model="toDate" min-date="toMinDate"></select-date>
<span class="select-date">
<input type="text" readonly="readonly" datepicker-popup="dd.MM.yyyy" datepicker-options="{startingDay: 1, showWeeks: true}" ng-model="date" show-button-bar="false" current-text="Heute" close-text="Schließen" is- open="dateStatus.opened" min-date="setMinDate" class="form-control" required="required" ng-click="openDatePopup()">
</span>
Below are changes in your directive.
.directive('selectDate', ['moment', function(moment) {
return {
restrict: 'E',
require:'^ngModel',
templateUrl: 'views/selectDate.html',
replace: true,
scope: {
ngModel: '='
minDate: '#'//which date input we are changing...
},
link: function($scope, element, attrs) {
$scope.date = $scope.ngModel;
$scope.selectedDateType = $scope.minDate=='fromMinDate'?'From':'To';
$scope.dateOptions = {
startingDay: 1,
showWeeks: false
};
$scope.dateStatus = {
opened: false
};
$scope.openDatePopup = function() {
$scope.dateStatus.opened = true;
$scope.setMinDate = new Date();//for From date..
};
$scope.$watch('date', function (newValue, oldValue) {
if (newValue !== oldValue) {
var date = moment(newValue);
$scope.ngModel = date.format('YYYY-MM-DD');
if($scope.selectedDateType=='From'){
$scope.setMinDate = $scope.fromDate;//for To date
}
}
});
}
};

datetimepicker bootstrap minDate and maxDate in observable knockout

I am using the plugin bootstrap-datetimepicker with knockout js. I've done a handler to dynamically manage and MaxDate MinDate, as I show below:
ko.bindingHandlers.dateTimePicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().dateTimePickerOptions || {};
$(element).datetimepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "dp.change", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
if (event.date && event.date != null && !(event.date instanceof Date)) {
value(event.date.toDate());
} else {
//value(event.date);
value(undefined);
}
}
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
var picker = $(element).data("DateTimePicker");
if (picker) {
picker.destroy();
}
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var picker = $(element).data("DateTimePicker");
//when the view model is updated, update the widget
if (picker) {
//Usamos moment para convertir a fecha ya que utiliza este plugin adicional datetimepicker
var koDate = moment(ko.utils.unwrapObservable(valueAccessor()) || '');
if (koDate.isValid()) picker.date(koDate.toDate() || null);
}
}
};
ko.bindingHandlers.minDate = {
update: function (element, valueAccessor) {
var value = moment(ko.utils.unwrapObservable(valueAccessor()) || ''),
picker = $(element).data("DateTimePicker");
if (value.isValid() && picker) {
picker.minDate(value.toDate());
}
}
};
ko.bindingHandlers.maxDate = {
update: function (element, valueAccessor) {
var value = moment(ko.utils.unwrapObservable(valueAccessor()) || ''),
picker = $(element).data("DateTimePicker");
if (value.isValid() && picker) {
picker.maxDate(value.toDate());
}
}
};
I use it as follows:
<div class="input-group" data-bind="dateTimePicker: NuevaTarea.FechaInicio, dateTimePickerOptions: { format: 'L', showClear: true }, minDate: NuevaTarea.minDate, maxDate: NuevaTarea.maxDate">
<input type="text" class="form-control" />
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
</div>
The problem is when change the NuevaTarea.minDate and NuevaTarea.maxDate, the date of MaxDate is assigned to NuevaTarea.FechaInicio.
Someone could help me create an observable MaxDate MinDate and properly functioning? No I'm doing wrong.
The version of plugin is 4.15.35.
https://github.com/Eonasdan/bootstrap-datetimepicker
ko.bindingHandlers.dateTimePicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().dateTimePickerOptions || {};
$(element).datetimepicker(options);
//when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "dp.change", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
if (event.date && event.date != null && !(event.date instanceof Date)) {
value(event.date.toDate());
} else {
//value(event.date);
value(undefined);
}
}
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
var picker = $(element).data("DateTimePicker");
if (picker) {
picker.destroy();
}
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var picker = $(element).data("DateTimePicker");
//when the view model is updated, update the widget
if (picker) {
//Usamos moment para convertir a fecha ya que utiliza este plugin adicional datetimepicker
var koDate = moment(ko.utils.unwrapObservable(valueAccessor()) || '');
if (koDate.isValid()) picker.date(koDate.toDate() || null);
}
}
};
ko.bindingHandlers.minDate = {
update: function (element, valueAccessor) {
var value = moment(ko.utils.unwrapObservable(valueAccessor()) || ''),
picker = $(element).data("DateTimePicker");
if (value.isValid() && picker) {
picker.minDate(value.toDate());
}
}
};
ko.bindingHandlers.maxDate = {
update: function (element, valueAccessor) {
var value = moment(ko.utils.unwrapObservable(valueAccessor()) || ''),
picker = $(element).data("DateTimePicker");
if (value.isValid() && picker) {
picker.maxDate(value.toDate());
}
}
};
function ViewModel() {
var self = this;
self.NuevaTarea = {
FechaInicio: ko.observable(),
FechaFin: ko.observable(),
minDate: ko.observable(),
maxDate: ko.observable()
};
self.NuevaTarea.minDate(new Date()) ;
//Not is working apply value null, help here!! I Like that this field is empty on start
self.NuevaTarea.FechaInicio(null);
}
ko.applyBindings(new ViewModel());
body{height:300px; padding:20px;}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.15.35/css/bootstrap-datetimepicker.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.15.35/js/bootstrap-datetimepicker.min.js"></script>
<div class="form-group">
<label class="control-label">Min Date:</label>
<div class="input-group" data-bind="dateTimePicker: NuevaTarea.minDate, dateTimePickerOptions: { format: 'L', showClear: true }">
<input type="text" class="form-control" />
<div class="input-group-addon">
<i class="glyphicon glyphicon-calendar"></i>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label">Fecha Inicio:</label>
<div class="input-group" data-bind="dateTimePicker: NuevaTarea.FechaInicio, dateTimePickerOptions: { format: 'L', showClear: true }, minDate: NuevaTarea.minDate, maxDate: NuevaTarea.maxDate">
<input type="text" class="form-control" />
<div class="input-group-addon">
<i class="glyphicon glyphicon-calendar"></i>
</div>
</div>
</div>
Thanks for your help but I already solved only just set, useCurrent: false options.
<div class="input-group" data-bind="dateTimePicker: NuevaTarea.FechaInicio, dateTimePickerOptions: { format: 'L', showClear: true, useCurrent: false }, minDate: NuevaTarea.minDate, maxDate: NuevaTarea.maxDate">
<input type="text" class="form-control" />
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
</div>

Angular typeahead asynchronous results - receiving error "TypeError: Cannot use 'in' operator to search for ... in ..."

I have made a directive for an Angular UI typeahead field. I am trying to design it so that as a user is typing, it sends asynchronous backend calls for results that will populate the dropdown that appears, as demonstrated in the Angular-bootstrap docs, example 2. However, when I start typing ('a' in this case), I get the error:
TypeError: Cannot use 'in' operator to search for 'getters' in a
Here is the factory method that makes the REST call:
certFactory.getUsersWithMatchingUsername = function(username) {
return $http.get(urlBase + '/managed/user?_queryFilter=userName+co+' + '\'' + username + '\'', {timeout: timeoutLength})
.then(function(response) {
// success
return response.data;
}, function(response) {
// error
return $q.reject(response);
});
};
Here is the controller method that calls the factory method:
$scope.getters = {
getUsersWithUsername: function (username) {
certFactory.getUsersWithMatchingUsername(username)
.then(function (response) {
var users = [];
angular.forEach(response.result, function(item) {
users.push(item);
})
return users;
}, function (error) {
console.log('failed!')
})
}
Here is my directive:
angular.module('myApp')
.directive('dropdownsearch', function(){
return {
restrict: 'E',
transclude: true,
scope: {
getterFn: '&',
config: '=', // object with properties label and type
disabled: '=?ngDisabled',
required: '=?ngRequred',
ngModel: '=',
options: '='
},
require: ['^form', 'ngModel'],
templateUrl: 'views/templates/dropdownSearchView.html',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
if (iAttrs.required !== undefined) {
// If attribute required exists
// ng-required takes a boolean
scope.required = true;
}
if (iAttrs.readonly !== undefined) {
// If attribute required exists
// ng-required takes a boolean
scope.readonly = true;
}
}
}
}
);
Here is the directive template:
<div class="form-group has-feedback">
<label class="control-label"> Choose {{ config.type }}></label>
<div class="dropdown dropdown">
<div class="input-group">
<input
type="text"
class="form-control"
placeholder="Make selection"
ng-model="ngModel"
uib-typeahead="option as option[config.label] for option in getterFn($viewValue)"
typeahead-editable="false"
ng-required="required"
ng-disabled="disabled"
/>
</div>
</div>
</div>
And finally, here is my directive in use:
<dropdownsearch ng-show= 'fieldMethods.showSubfield(subfield)'
getter-fn="getters.getUsersWithUsername"
ng-model="subsubfield.value"
config="fieldMethods.getConfig(subfield)">
</dropdownsearch>
Any help would be greatly appreciated. Also, let me know if any additional info is required.
The UI Bootstrap website Asynchronous Typeahead example uses uib-typeahead="address for address in getLocation($viewValue)". My guess is that your error message is saying that it's expecting for, not as in the directive. I don't understand this so I could be wrong! :-)

angular form validation not working

I have this simple form with a text box that is required and a save button.
<form role="form" name="frmVariableConfig" novalidate ng-submit="frmVariableConfig.$valid && vm.saveChanges()">
<input type="text" ng-model="vm.CurrCustomer.Name" name="txtCustomerName" class="form-control input-sm validate[required]" placeholder="txtCustomerName" check-validation>
<button type="submit" class="btn btn-sm text-right">Save</button>
</form>
I'm using this directive to activate the Jquery Validation Engine
angular.module('app').directive('checkValidation', [
function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
element.closest('form').validationEngine(
{
promptPosition: 'centerRight',
scroll: true, prettySelect: true,
autoPositionUpdate: true,
//validateNonVisibleFields: true,
//autoHidePrompt: true,
autoHideDelay: 1000,
fadeDuration: 0.9
}
);
}
};
}]);
but it keeps on calling saveChanges() even if the text-box is empty. It should not call this function if the text-box is empty.
Please help.
In one project, the only way I found to solve this scenario was to pass the value of $valid to save method.
HTML
ng-submit="vm.saveChanges( frmVariableConfig.$valid )"
JS
$scope.saveChanges = function(valid) {
if(valid) {
//do save
}
}

Categories