AngularJS: How to edit parts of a datetime from different html controls - angularjs

I would like to have 2 textboxes assigned to the same ng-model initialized to Date.now() and allow them to be edited by the user.
My template looks like:
<div ng-controller="MainCtrl">
<input type="text" ng-model="date | date:'yyyy-MM-dd'" />
<input type="text" ng-model="date | date:'HH:mm'" />
<button ng-click="submit()">Submit</button>
<div ng-if="show">
{{ date }}
</div>
</div>
And my controller looks like:
angular.module('app', []).controller('MainCtrl', function($scope) {
$scope.show = false;
$scope.date = new Date(Date.now());
$scope.submit = function () {
$scope.show = true;
}
});
Plunker: http://plnkr.co/edit/d2slQW6pDTLM4KTo
In my example, the textboxes are not editable, and I get a console error stating the expression is non-assignable.
How can I modify this example to allow the date parts to be modifiable?

If you're willing to use date and time inputs instead of text, this is very easy to do.
<input type="date" ng-model="date" />
<input type="time" ng-model="date" />
Because they're bound to the same Date instance, leap days daylight savings (where a day might be 23 or 25 hours) are supported.
If you don't like the native date and time inputs and want some more control over the style/functionality and consistent UI across devices, you can try Kendo UI's DatePicker and TimePicker widgets. They both have AngularJS support and are included in the free offering called Kendo UI Core.
And if you want to avoid having milliseconds in your time picker, make sure they're stripped from whatever Date instance is bound to your scope property.
$scope.date = new Date(Date.now());
$scope.date.setMilliseconds(0)
As an aside, I don't believe filters work in ng-model expressions, which is probably the source of your error message.

Related

How to format a date from the date object in `ng-model`

I am getting date values from the ng-model from the controller. I would like to format the date as 10-22-2013 But in the output i am getting the date format as 10/22/2013
what is the correct way to format the date here?
js :
angular.module('dateInputExample', [])
.controller('DateController', ['$scope', function($scope) {
$scope.example = {
value: new Date(2013, 9, 22)
};
}]);
html :
<form name="myForm" ng-controller="DateController as dateCtrl">
<label for="exampleInput">Pick a date </label>
<input type="date" id="exampleInput" name="input" ng-model="example.value"
placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
<div role="alert">
<span class="error" ng-show="myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.date">
Not a valid date!</span>
</div>
</form>
Live Demo
Using purely HTML5 there is no answer to your question. Refer to this link.
There is no way possible to change the format
We have to differentiate between the over the wire format and the
browser's presentation format.
Wire format The HTML5 date input specification 1 refers to the
RFC3339 specification 2, which specifies a full-date format equal
to: yyyy-mm-dd. See section 5.6 of the RFC3339 specification for more
details.
Presentation format Browsers are unrestricted in how they present a
date input. At the time of writing Chrome has the most extensive date
support [3]. It displays a date picker using the user's local calendar
format. Opera (v10.6+) also displays a date picker, but shows the date
in the wire format. Other browsers, such as Firefox 44.0.2 and
Internet Explorer 9/10/11 display a text input field with the wire
format.
References http://www.w3.org/TR/html-markup/input.date.html
https://www.rfc-editor.org/rfc/rfc3339
https://plus.google.com/102860501900098846931/posts/hTcMLVNKnec
I suggest using angular-ui. It has a neat load of modules that makes everything easy for angular.
The markup in the view would look like this:
<p class="form-group">
<label>Result</label>
<input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="date" />
</p>
And on the controller:
angular.module('ui.bootstrap.demo').controller('DateParserDemoCtrl', function ($scope, uibDateParser) {
$scope.format = 'MM-dd-yyyy';
$scope.date = new Date();
});

Reduce the number of model updates with a color picker

I use a standard HTML color picker in my application:
<input type="color" ng-model="vm.currentUser.color" />
If I click the button of that color picker and manually change the color, then the model is updated very often. Since I have a watch on vm.currentUser.color, the corresponding method is also invoked very often, and that's problematic.
Is there any way to set the model variable only when the OK button of the color picker is clicked?
You can use ng-change on that field as it triggers only after the color popup is closed and if a change occurs (no matter how many times you change the color inside the popup): http://plnkr.co/edit/AjDgoaUFky20vNCfu04O?p=preview
angular.module('app', [])
.controller('Ctrl', function($scope, $timeout) {
$scope.x = '#ff0000';
$scope.res = '';
$scope.a = function(x) {
console.log(x);
$scope.res = 'Color changed to ' + x;
$timeout(function(){$scope.res = ''}, 2000);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="Ctrl">
<input type="color" ng-model="x" ng-change="a(x)">
<p>{{res}}</p>
</div>
Since AngularJS 1.3.0-beta.6, there is a ngModelOptions directive, perfectly suited to your problem. It allows you to "tune how model updates are done", which is perfectly what you're looking for.
For instance, you can update the model only if the value doesn't change after a reasonable amount of time. I'll use a value of 500 milliseconds here in order to make the effect really apparent, but in practice, a lower one seems more appropriate.
<input
type="color"
ng-model="color"
ng-model-options="{debounce: 500}"
/>

AngularJS binding to date inputs

I have a scope binding to a HTML5 date input. The scope binds fine when I select a date using the calendar pop-out, or when I manually type in a date. But the binding is gone whenever I change the month using the arrow keys. How do I fix this?
http://jsfiddle.net/hientc/5zzwL30z/1/
<div ng-app="myApp">
<div ng-controller="myController">
<input type="date" ng-model="date" />
{{ date }}
</div>
</div>
app.controller('myController', function ($scope, $filter) {
$scope.date = $filter('date')(new Date(), 'yyyy-MM-dd');
});
Since a lot of people are confused, here's a image showing the steps.

Simple angularjs date input

I have an edit page for an event and one of my fields is a date. In some browsers it seems to act like a plain text box (IE8), however in chrome it displays "dd/mm/yyyy" and if you click on it it has some additional options for setting the date.
My issue though is on the edit page it's not populating the existing date (I imagine because it's not in the correct format?). The MVC controller returns the data in this format "2014-03-08T00:00:00" (just using basic CRUD controller actions).
<form name="form" class="form-horizontal">
<div class="control-group" ng-class="{error: form.EventDate.$invalid}">
<label class="control-label" for="EventDate">Event Date</label>
<div class="controls">
<input type="date" ng-model="item.EventDate" id="EventDate">
</div>
</div>
<div class="form-actions">
<button ng-click="save()" class="btn btn-primary">
{{action}}
</button>
Cancel
</div>
</form>
I've seen quite a few posts on using directives and watches, but that seems complicated. I would have thought there would have been a relatively simple way of formatting the model data so that it displays in the right format and works as expected. I don't mind if Chrome gives a different experience than other browsers - it's just a simple internal user website. I just don't like that it's not prepopulating the date when I edit a record.
If you want to populate the field with an initial value, then the following will work
//Controller:
$scope.myDate = new Date('2014-03-08T00:00:00');
//HTML:
<input type="date" ng-init="model=(myDate | date:'yyyy-MM-dd')" ng-model="model" />
However, I strongly recommend creating a custom date field directive.
A custom input field directive offers the following benefits:
Two-way binding between the model and the view.
For example, when you enter a valid date in the input field, it will automatically assign a javascript date to the model; and when you assign a valid javascript date as the model, it will automatically format it in the text field.
Form validation support. When you enter an invalid date, you can set an $error flag, which can be used in your view bindings (i.e. displaying an error message). Setting an error flag will also set form.$valid to false so that you can conditionally submit the form to the server.
There are three basic things to consider when implementing a custom date directive:
A parser that will parse the input text and return the model
(in this case, a javascript date).
A formatter that will format the model and display it in the text field.
Setting of an optional validation flag which can be used in the UI
for custom form validation.
Date Directive:
myApp.directive('dateField', function($filter) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
ngModelController.$parsers.push(function(data) {
//View -> Model
var date = Date.parseExact(data,'yyyy-MM-dd');
// if the date field is not a valid date
// then set a 'date' error flag by calling $setValidity
ngModelController.$setValidity('date', date!=null);
return date == null ? undefined : date;
});
ngModelController.$formatters.push(function(data) {
//Model -> View
return $filter('date')(data, "yyyy-MM-dd");
});
}
}
});
Note: For parsing dates, this directive uses Date.js (an external library).
CSS:
.error {
color:red;
}
.error-border {
border: solid 2px red;
}
HTML:
<form name="myForm">
<input ng-class="{'error-border': myForm.myDate.$error.date}" type="date"
name="myDate" ng-model="myDate" date-field />
<span ng-show="myForm.myDate.$error.date" class="error">
Please enter a valid date!!!
</span>
<br /> Raw Date: {{myDate}}
<br /> Formatted Nicely: {{ myDate | date:'yyyy, MMMM dd'}}
<br /> Is Valid Date? {{ !myForm.myDate.$error.date}}
<br /> Is Form Valid? {{ myForm.$valid }}
</form>
Controller:
myApp.controller('ctrl', function($scope) {
$scope.myDate = new Date('2014-03-08T00:00:00');
});
Demo JS Fiddle with Date.js
Demo JS Fiddle with Moment.js

Conditional tooltip with Bootstrap 3 and angular

So I can get tooltips to appear on field focus, but I only want them to sometimes. I want to add a manual trigger, but to say the docs are lacking would be to indicate that some exist. Here's what I've found so far (in the source)
// Default hide triggers for each show trigger
var triggerMap = {
'mouseenter': 'mouseleave',
'click': 'click',
'focus': 'blur'
};
...
/**
* This allows you to extend the set of trigger mappings available. E.g.:
*
* $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
*/
this.setTriggers = function setTriggers ( triggers ) {
angular.extend( triggerMap, triggers );
};
So, how do you write one of these triggers?
if you are still searching for a solution it might be handy to know that the tooltips only show when there is a text value, otherwise they don't actually show.
I myself use the popover directive, here is a plunker where you can edit your text. When you clear the field, the tooltip won't show.
http://plnkr.co/edit/Zdkyhc90qTZFzLEwtrVL?p=preview
<body ng-controller="MainCtrl">
<br/><br/>
<input type="text" size="100" ng-model="error"/><br/><br/>
<p class="btn btn-default"
popover="{{error}}"
popover-placement="right"
popover-trigger="mouseenter">Hover my error!</p>
</body>
And in the controller you just set the error initial value. Make sure you include 'ui.bootstrap' as a dependency for your app, else it won't work.
var app = angular.module('plunker', ['ui.bootstrap']);
app.controller('MainCtrl', function($scope) {
$scope.error = 'Something went wrong';
});
AngularJS 1.5.7 and Bootstrap 3.3.6 support uib-tooltip-html properties on input, select and textarea elements. Unlike uib-tooltip properties, uib-tooltip-html properties support expressions. You should be able to express your conditions therein.
For example, here is a date of birth textbox with an expression that either names the field when valid OR explains the validation error:
<input id="dateOfBirth{{::vm.studentID}}"
name="dateOfBirth"
placeholder="Date of Birth"
uib-tooltip-html="myFormName.dateOfBirth.$valid ? 'Date of Birth' : myFormName.dateOfBirth.$error.required ? 'Date of Birth is required' : 'Date of Birth is not a valid date: mm/dd/yyyy'"
type="date"
class="form-control"
data-ng-model="vm.dateOfBirth"
data-ng-required="vm.editMode"
min="1920-01-01"
data-ng-max="vm.maxDateOfBirth"
tabindex="3" />
With a little more work you could explain the min and max date errors as well.
<label>
Open tooltips <span uib-tooltip="Hello!" tooltip-is-open="tooltipIsOpen" tooltip-placement="bottom">conditionally.</span>
</label>
Check the tooltip part of the API doc
we can use popover-enable property to show it conditional
This can be archived through Angular Property binding and the title property of the bootstrap element. In Bootstrap, a tooltip will only show in the DOM if some text was given for the title value. What you have to do is to bind property or method to the tooltip. Here getTooltip() should return either tooltip or empty string.
<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" [title]="getTooltip()">

Resources