Odd behaviour with multiple datepickers using AngularJS and UI Bootstrap - angularjs

I'm writing an app using AngularJS and UI Bootstrap, and I'm trying to implement a modal where the user can choose a start date and an end date for an event. The problem I'm having is that when the user is chosing an end date, the start date is affected as well, leading to incorrect values.
I have made a minimal test case here: http://147.75.205.135/test/
(I tried to reproduce it using Plunkr, but for some reason I didn't get the same behaviour there)
Plunker link: http://plnkr.co/edit/kika1viHi8csXMuklw8Z
To recreate the issue, click the "Launch" button, open up the datepicker for the start date, and click the right arrow to go to the next month and choose a date. Then for the end date, click the right arrow to go to the next month and pick any date. Then click "Save". An alert box will show up displaying the start and end date, but the start date does not correspond to the one chosen in the datepicker, it is set to the 1st of the month chosen for the end date.
I have nailed it down to the following function in http://147.75.205.135/test/script/angular/main.js:
$scope.adjustEndDate = function() {
if ($scope.endDate < $scope.startDate) {
console.log("Adjusting end date to " + $scope.startDate);
$scope.endDate = $scope.startDate;
}
};
It is called from ng-change on the start date picker. Commenting it out makes the odd behaviour go away. But I don't understand why it would cause this issue. I need this function so that when the user has chosen a start date, the end date is automatically adjusted so that it is never before the start date.
Complete code:
index.html
<!doctype html>
<html>
<head>
<title>Test page</title>
<link rel="stylesheet" href="bootswatch-dist/css/bootstrap.min.css"></link>
<link rel="stylesheet" href="css/main.css"></link>
<script>
var conf = {
templates: {
datepickModal: "script/angular/templates/datepickModal.html"
}
};
</script>
<script src="angular/angular.js" ></script>
<script src="jquery/dist/jquery.min.js"></script>
<script src="angular-animate/angular-animate.js"></script>
<script src="angular-bootstrap/ui-bootstrap.js"></script>
<script src="angular-bootstrap/ui-bootstrap-tpls.js"></script>
<script src="script/angular/main.js"></script>
</head>
<body data-ng-app="mainApp">
<div id="main" ng-controller="testCtrl">
<button ng-click="openModal()">Launch modal</button>
</div>
</body>
</html>
main.js
+function(angular, d3) {
"use strict";
var app = angular.module('mainApp', ['ui.bootstrap']);
app.controller('datepickCtrl', ['$scope', '$rootScope', '$uibModalInstance', function($scope, $rootScope, $uibModalInstance) {
$scope.startDate = new Date();
$scope.endDate = new Date();
$scope.startOpened = false;
$scope.endOpened = false;
$scope.name = '';
$scope.mode = 'list';
$scope.save = function () {
alert("Saving dates start: " + $scope.startDate + " and end " + $scope.endDate);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.toggleStartCalendar = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.startOpened = !$scope.startOpened;
};
$scope.toggleEndCalendar = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.endOpened = !$scope.endOpened;
};
$scope.dateOptions = {
formatYear: 'yy',
startingDay: 1
};
$scope.setMode = function(mode) {
$scope.mode = mode;
}
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.adjustEndDate = function() {
if ($scope.endDate < $scope.startDate) {
console.log("Adjusting end date to " + $scope.startDate);
$scope.endDate = $scope.startDate;
}
};
$scope.adjustStartDate = function() {
if ($scope.startDate > $scope.endDate) {
console.log("Adjusting start date to " + $scope.endDate);
$scope.startDate = $scope.endDate;
}
};
}]);
app.controller('testCtrl', ['$scope', '$rootScope', '$uibModal', function($scope, $rootScope, $uibModal) {
$scope.openModal = function() {
if (conf.suspended) {
return;
}
var modalInstance = $uibModal.open({
animation: true,
templateUrl: conf.templates.datepickModal,
controller: 'datepickCtrl',
size: 'md'
});
};
}]);
}(window.angular, window.d3);
modal.html
<div class="modal-header">
<h3 class="modal-title">Datepick tester</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<p class="input-group">
<label for="startdate">Start date</label><p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup ng-model="startDate" ng-change="adjustEndDate()" is-open="startOpened" ng-required="true" uib-datepicker-options="dateOptions" name="startdate" ng-click="toggleStartCalendar($event)"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="toggleStartCalendar($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
<div class="col-md-6">
<p class="input-group">
<label for="enddate">End date</label><p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup ng-model="endDate" is-open="endOpened" ng-change="adjustStartDate()" ng-required="true" uib-datepicker-options="dateOptions" name="enddate" ng-click="toggleEndCalendar($event)"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="toggleEndCalendar($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="save()">Save</button>
<button class="btn btn-warning" ng-click="cancel()">Close</button>
</div>
Versions used
AngularJS: 1.4.8
JQuery: 2.1.4
UI-bootstrap: 1.2.4

I had the same problem. The issue is that you are copying the Date object here:
$scope.endDate = $scope.startDate
Just change it to:
$scope.endDate = new Date($scope.startDate.getTime())

Related

How to make uib-datepicker's show-spinners to be dynamically updated?

As you can see in that snippet, I've set the attributes show-meridian and show-spinners to match to the variable $scope.myBool.
I've added as well a green button that says change! and toggles $scope.myBool.
While show-meridian is perfectly reacting to any change in $scope.myBool, show-meridian is not being updated.
<!doctype html>
<html ng-app="ui.bootstrap.demo">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.0/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-sanitize/1.5.9/angular-sanitize.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.3.0/ui-bootstrap-tpls.min.js"></script>
<script>
angular.module('ui.bootstrap.demo', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('TimepickerDemoCtrl', function($scope, $log) {
$scope.mytime = new Date();
$scope.hstep = 1;
$scope.mstep = 15;
$scope.options = {
hstep: [1, 2, 3],
mstep: [1, 5, 10, 15, 25, 30]
};
$scope.ismeridian = true;
$scope.toggleMode = function() {
$scope.ismeridian = !$scope.ismeridian;
};
$scope.update = function() {
var d = new Date();
d.setHours(14);
d.setMinutes(0);
$scope.mytime = d;
};
$scope.changed = function() {
$log.log('Time changed to: ' + $scope.mytime);
};
$scope.clear = function() {
$scope.mytime = null;
};
$scope.myBool = false;
});
</script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<style>
.timepickercontainer .uib-timepicker .btn-link {
display: none;
}
</style>
</head>
<body>
<div ng-controller="TimepickerDemoCtrl">
<div class="timepickercontainer">
<div uib-timepicker ng-model="mytime" ng-change="changed()" arrowkeys="false" hour-step="hstep" minute-step="mstep" show-meridian="myBool"
show-spinners="!myBool" ></div>
</div>
<pre class="alert alert-info">Time is: {{mytime | date:'shortTime' }}</pre>
<div class="row">
<div class="col-xs-6">
Hours step is:
<select class="form-control" ng-model="hstep" ng-options="opt for opt in options.hstep"></select>
</div>
<div class="col-xs-6">
Minutes step is:
<select class="form-control" ng-model="mstep" ng-options="opt for opt in options.mstep"></select>
</div>
</div>
<hr>
<button type="button" class="btn btn-info" ng-click="toggleMode()">12H / 24H</button>
<button type="button" class="btn btn-default" ng-click="update()">Set to 14:00</button>
<button type="button" class="btn btn-danger" ng-click="clear()">Clear</button>
<button class='btn btn-success' ng-click='myBool = !myBool'>change!</button>
</div>
</body>
</html>
This is a hacky soluction. how i say in the comment is better to maka a issue in the repo of the directive.
for make spinner hide and show you must enter in the ui-bootstrap-tpls.js file and find this line
$scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
$scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
and substitute with this
$scope.showSpinners = timepickerConfig.showSpinners;
if ($attrs.showSpinners) {
watchers.push($scope.$parent.$watch($parse($attrs.showSpinners),
function(value) {
$scope.showSpinners = !!value;
updateTemplate();
}));
}
heres is my plnkr
example

Angular UI Bootstrap Datetimepicker with seconds

I want to get bootstrap datetimepicker input value in angularjs controller. I am using following custom directive to achieve this . But i am getting the value is undefined. I have added the code related to html and angular code related to the issue.
HTML
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.43/css/bootstrap-datetimepicker.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.15.2/locale/en-gb.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.43/js/bootstrap-datetimepicker.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-resource.js"></script>
<div class="form-group">
<div class='input-group date' id='datetimepickerFrom' datetimepicker
ng-model="fromDate" >
<input type='text' class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
JS
'use strict';
var App = angular.module('app',['ngResource']);
App.directive('datetimepicker', function(){
return {
require: '?ngModel',
restrict: 'A',
link: function(scope, element, attrs, ngModel){
if(!ngModel) return; // do nothing if no ng-model
ngModel.$render = function(){
element.find('#datetimepickerFrom').val( ngModel.$viewValue || '' );
};
element.datetimepicker({
format : 'YYYY-MM-DD HH:mm:ss'
});
element.on('dp.change', function(){
scope.$apply(read);
});
read();
function read() {
var value = element.find('#datetimepickerFrom').val();
ngModel.$setViewValue(value);
console.log(ngModel.$setViewValue(value));
}
}
};
});
App.controller('myController', ['$scope', function($scope) {
$scope.fromDate = moment();
}]);
I have found a package for you to fix your problem which is in the link.
LINK
Easy way to achieve it follow these below steps
Add this markup.
You need to include the following scripts
//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js
//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.0.1.js
//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css
//cdn.rawgit.com/zhaber/datetimepicker/master/datetimepicker.js
//cdn.rawgit.com/zhaber/datetimepicker/master/datetimepicker.css
Controller will hold the model value in $scope.date which contains the date and time.
Update 1
This control does not support Seconds, I would suggest you to go with Angular UI Bootstrap as below
Include the above references expect last two and use the below markup.
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup is-open="popup1.opened" ng-model="fromDate" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
<div uib-timepicker ng-model="fromTime" ng-change="changed()" hour-step="1" minute-step="1" show-meridian="ismeridian" show-seconds="true"></div>
Add this module as a dependency ui.bootstrap
Use the below code to combine your value
$scope.changed = function () {
var month = $scope.fromDate.getMonth()+1;
var day = $scope.fromDate.getDate();
var year = $scope.fromDate.getFullYear();
var hour = $scope.fromTime.getHours();
if (hour < 10)
hour = "0"+hour;
var min = $scope.fromTime.getMinutes();
if (min < 10)
min = "0"+min;
var sec = $scope.fromTime.getSeconds();
if (sec < 10)
sec = "0"+sec;
//put it all togeter
var dateTimeString = month+'/'+day+'/'+year+' '+hour+':'+min+':'+sec;
$scope.fromDateTime= dateTimeString;
};
Your live ng-model value combined together
fromDateTime:{{fromDateTime |date : 'yyyy-MM-dd HH:mm:ss'}}
So by this if the seconds value is changed only you will get the actual Date Time

ng-if not really as intended

I'm trying to make a page print with Angular, using ng-if.
$scope.onBtnPrintClicked = function(journal, date) {
$scope.printTransaction = false;
$scope.current = new Date();
window.print()
};
$scope.onBtnPrintTransactionClicked = function(journal, date) {
$scope.printTransaction = true;
$scope.current = new Date();
window.print()
};
<button ng-click="onBtnPrintTransactionClicked(journal, selected.date)" class="btn btn-default btn-sm"><i class="fa fa-print"></i> Print</button>
<button ng-click="onBtnPrintClicked(journal, selected.date)" class="btn btn-default btn-sm"><i class="fa fa-print"></i>Print 2</button>
<section ng-if="printTransaction" class="printable journal-container">
<h1>PRINT</h1>
</section>
<section ng-if="!printTransaction" class="printable journal-container">
<div class="row">
<div class="col-sm-12">PRINT2</div>
</div>
</section>
But when I try to click print/print 2 it's showing different page/content.
What could be the problem?
From what I could understand from your question is that you're trying to print the current page, however the changes performed by your functions aren't completely load yet, so you could use setTimeout to make a little delay, as below:
(function() {
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope'];
function MainCtrl($scope) {
$scope.printTransaction = true;
$scope.onBtnPrintClicked = function(journal, date) {
$scope.printTransaction = false;
$scope.current = new Date();
setTimeout(function() {
window.print();
}, 500);
};
$scope.onBtnPrintTransactionClicked = function(journal, date) {
$scope.printTransaction = true;
$scope.current = new Date();
setTimeout(function() {
window.print();
}, 500);
};
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css">
</head>
<body ng-controller="MainCtrl">
<button ng-click="onBtnPrintTransactionClicked(journal, selected.date)" class="btn btn-default btn-sm"><i class="fa fa-print"></i> Print</button>
<button ng-click="onBtnPrintClicked(journal, selected.date)" class="btn btn-default btn-sm"><i class="fa fa-print"></i> Print 2</button>
<section ng-if="printTransaction" class="printable journal-container">
<h1>PRINT</h1>
</section>
<section ng-if="!printTransaction" class="printable journal-container">
<div class="row">
<div class="col-sm-12">PRINT2</div>
</div>
</section>
</body>
</html>
Note: I don't know if these are your real functions, but you could simplify it and do it all in a single function.

Angular UI basic modal not working (demo from docs - with jsfiddle)

I am trying to learn angular ui and copy pasted a demo from their website into a jsfiddle. For some reason it's not working and not giving an error. Can anybody see what I am doing wrong?
If you go into the jsfiddle below and then click the open button nothing happens and there is no error.
JSfiddle: http://jsfiddle.net/baswg1wz/1/
Javascript:
angular.module('ui.bootstrap.demo', ['ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.open = function (size) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
HTML
<div ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<ul>
<li ng-repeat="item in items">
<a ng-click="selected.item = item">{{ item }}</a>
</li>
</ul>
Selected: <b>{{ selected.item }}</b>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
<button class="btn btn-default" ng-click="open()">Open me!</button>
<button class="btn btn-default" ng-click="open('lg')">Large modal</button>
<button class="btn btn-default" ng-click="open('sm')">Small modal</button>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui/0.4.0/angular-ui.min.js"></script>
It's not giving any errors because you didn't bootstrap your Angular application. You should wrap your entire html in a div (in a real page you should do this on the html or body tag) and then use the ng-app attribute to initialize/bootstrap your application:
<div ng-app="ui.bootstrap.demo">
<!-- your html -->
</div>
Also i noticed you didn't include the right ui library, you're using:
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui/0.4.0/angular-ui.min.js"></script>
You should be using: (also note the http://)
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js"></script>
Furhermore, when using Angular in JSFiddle you should use the no wrap - in <head> option.
That should get you a lot further, but i would recommend using Plunker, if you checked the modal example on the ui.bootstrap site you could have noticed the blue Edit in plunker on the topright of the code, try clicking that.
http://angular-ui.github.io/bootstrap/#/modal

How to call controller function from other controller angularjs

please look at this code. My button must call function checkResults() from controller CtrlTwo.
How to do this?
var myApp = angular.module('myApp', []);
function CtrlOne($scope){
$scope.greet = 'Hi! ';
}
function CtrlTwo($scope){
$scope.$emit('checkResults', function(){
alert('Function is called!');
}
);
}
<body ng-app="myApp">
<div ng-controller="CtrlOne">
{{greet}}
<input type="submit" class="btn btn-primary pull-right" value="Check results" ng-href='#here' ng-click='checkResults()'/>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</body>
Here, hopefully this example will help you:
Javascript:
var myApp = angular.module('myApp', []);
myApp.controller('CtrlOne', function($scope){
$scope.greet = "hi!"
var nTimes = 0;
$scope.$on('myEvent', function(){
$scope.greet = "Hi! The event has been called " + (++nTimes) + " times";
});
});
myApp.controller('CtrlTwo', function($scope){
$scope.emitCustomEvent = function(){ $scope.$emit('myEvent'); };
});
View:
<div ng-app="myApp">
<div ng-controller="CtrlOne">
{{greet}}
<div ng-controller="CtrlTwo">
<input type="button" value="Check results" ng-click="emitCustomEvent()" >
</div>
</div>
</div>
Working Example

Resources