How to use Material Design Lite and Angular for forms - angularjs

*Disclaimer: this question is not about using material design in an angular app but using material design lite inside a form. So, please, don't answer I should rather use angular material, materialize, lumx, material bootstrap, or daemonite... I know, they exist.*
With Angular a typical form field for a name would be:
<form name="myForm">
<label>
Enter your name:
<input type="text"
name="myName"
ng-model="name"
ng-minlength="5"
ng-maxlength="20"
required />
</label>
<div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
<div ng-message="required">You did not enter a field</div>
<div ng-message="minlength">Your field is too short</div>
<div ng-message="maxlength">Your field is too long</div>
</div>
</form>
With Material Design Lite, it would be something like that:
<form action="#">
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="text" id="user" pattern="[A-Z,a-z, ]*" />
<label class="mdl-textfield__label" for="user">User name</label>
<span class="mdl-textfield__error">Letters and spaces only</span>
</div>
</form>
Question: how is it possible to use the angular validation functionality combined with ngMessage (for multiple error messages) with the Material Design Lite?

You can write your own angular module to validate MDL input fields, here is a working example: http://codepen.io/alisterlf/pen/ZGgJQB
JS
// create angular app
var validationApp = angular.module('validationApp', ['fieldMatch']);
//Field Match directive
angular.module('fieldMatch', [])
.directive('fieldMatch', ["$parse", function($parse) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var me = $parse(attrs.ngModel);
var matchTo = $parse(attrs.fieldMatch);
scope.$watchGroup([me, matchTo], function(newValues, oldValues) {
ctrl.$setValidity('fieldmatch', me(scope) === matchTo(scope));
}, true);
}
}
}]);
//Run material design lite
validationApp.run(function($rootScope, $timeout) {
$rootScope.$on('$viewContentLoaded', function(event) {
$timeout(function() {
componentHandler.upgradeAllRegistered();
}, 0);
});
$rootScope.render = {
header: true,
aside: true
}
});
// create angular controller
validationApp.controller('mainController', function($scope) {
$scope.formStatus = '';
// function to submit the form after all validation has occurred
$scope.submit = function() {
// check to make sure the form is completely valid
if ($scope.form.$invalid) {
angular.forEach($scope.form.$error, function(field) {
angular.forEach(field, function(errorField) {
errorField.$setTouched();
})
});
$scope.formStatus = "Form is invalid.";
console.log("Form is invalid.");
} else {
$scope.formStatus = "Form is valid.";
console.log("Form is valid.");
console.log($scope.data);
}
};
});

Related

AngularJS: Hiding ng-message until hitting the form-submit button

This is a typical example of the use of ng-messages in AngularJS (1.x):
<form name="demoForm">
<input name="amount" type="number" ng-model="amount" max="100" required>
<div ng-messages="demoForm.amount.$error">
<div ng-message="required">This field is required</div>
</div>
<button type="submit">test submit</button>
</form>
see: http://jsfiddle.net/11en8swy/3/
I now want to change this example so the "This field is required" error only shows when the field is touched ($touched) or the user hits the submit button.
I cannot use the ng-submitted class on the form since the validation error prevents the submitting of the form.
How should I do this?
Thanks
You can do this using ng-show:
<div ng-messages="demoForm.amount.$error" ng-show="demoForm.amount.$touched">
<div ng-message="required">This field is required</div>
</div>
And use a custom directive. See a working demo:
var app = angular.module('app', ['ngMessages']);
app.controller('mainCtrl', function($scope) {
});
app.directive('hasFocus', function($timeout) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ctrl) {
element.on('focus', function() {
$timeout(function() {
ctrl.hasFocusFoo = true;
})
});
element.on('blur', function() {
$timeout(function() {
ctrl.hasFocusFoo = false;
})
});
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-messages.js"></script>
<body ng-app="app" ng-controller="mainCtrl">
<form name="demoForm">
<input name="amount" type="number" ng-model="amount" max="100" required has-focus>
<div ng-messages="demoForm.amount.$error" ng-show="demoForm.amount.$touched || demoForm.amount.hasFocusFoo">
<div ng-message="required">This field is required</div>
</div>
<button type="submit">test submit</button>
</form>
</body>
The directive is basically setting another hasFocusFoo field on the ngModel controller then we can easily use that directive.
Ah, at the PC at last.
https://plnkr.co/edit/EX3UmoAOKmTKlameBXRa?p=preview
<form name="mc.form">
<input type="text" name="empty" ng-model="mc.empty" required />
<label ng-show="mc.form.empty.$dirty && mc.form.empty.$error.required">i'm empty</label>
</form>
MainController.$inject = ['$timeout'];
function MainController($timeout) {
var vm = this;
$timeout(function(){
vm.form.$setPristine();
});
vm.submit = function(){
if(vm.form.$valid){
alert('yay');
}else{
(vm.form.$error.required || []).forEach(function(f){
f.$dirty = true;
});
}
}
}
Here is how I handle this task in my solution. form.$setPristine() - sets the field in a pristine state, so field isn't $dirty and error hidden. But after submit I manually state required fields in a $dirty state, so errors become visible. + if you type something, and delete it after, the error would be visible without submitting a form.

How to validate forms with dynamic ngMessages

This plunk has a form with a field that only allows to enter aaa. Note that the error message is set in the controller, not in the html. When the user clicks on Submit they should see the message, but the message is not shown. What's wrong with this code?
HTML
<body ng-app="ngMessagesExample" ng-controller="ctl">
<form name="myForm" novalidate ng-submit="submitForm()">
<label>
This field is only valid when 'aaa' is
<input type="field1"
ng-model="data.field1"
name="field1"
required />
</label>
<div ng-messages="myForm.field1.$error" style="color:red">
<div ng-message-exp="required">{{errorMsg}}</div>
</div>
<br/><br/>
<button style="float:left" type="submit">Submit</button>
</form>
</body>
Javascript
var app = angular.module('ngMessagesExample', ['ngMessages']);
app.controller('ctl', function ($scope) {
$scope.submitForm = function() {
if ($scope.field1 != 'aaa')
$errorMsg = "This field should be 'aaa'";
else
$errorMsg = "";
};
});
Forget my previous answer.
Easiest and most robust is actually to make a new directive.
var app = angular.module('ngMessagesExample', ['ngMessages']);
app.directive("aaa", function() {
return {
restrict: "A",
require: "ngModel",
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.aaa = function(modelValue) {
return modelValue === 'aaa';
}
}
};
});
And your controller:
app.controller('ctl', function ($scope) {
$scope.data = {
field1: ""
}
$scope.submitForm = function(){
//extra whatever code
}
});
Your HTML should be this:
<body ng-app="ngMessagesExample" ng-controller="ctl">
<form name="myForm" novalidate ng-submit="submitForm(myForm)">
<label>This field is only valid when 'aaa' is</label>
<input type="field1"
ng-model="data.field1"
name="field1"
required aaa/>
<div ng-messages="myForm.field1.$error" style="color:red">
<div ng-message="required">FIELD IS REQUIRED!!</div>
<div ng-message="aaa">FIELD MUST BE 'aaa'</div>
</div>
<button style="float:left" type="submit">Submit</button>
</form>
</body>

How do I get my dual binding to work properly with bootstrap datetime picker? [duplicate]

Here is the html for the date field :
<div class='form-group'>
<label>Check out</label>
<input type='text' ng-model='checkOut' class='form-control' data-date-format="yyyy-mm-dd" placeholder="Check out" required id="check-out">
</div>
<script>
$('#check-out').datepicker();
</script>
The datepicker shows up in the input field. However if I do this in my controller :
console.log($scope.checkOut);
I get undefined in the javascript console.
How to solve this ?
Is there a better way to use bootstrap-datepicker with angularjs ?
I don't want to use angular-ui/angular-strap since my project is bloated with javascript libraries.
As #lort suggests, you cannot access the datepicker model from your controller because the datepicker has its own private scope.
If you set: ng-model="parent.checkOut"
and define in the controller: $scope.parent = {checkOut:''};
you can access the datepicker using: $scope.parent.checkOut
I am using bootstrap 3 datepicker https://eonasdan.github.io/bootstrap-datetimepicker/ and angularjs, I had the same problem with ng-model, so I am getting input date value using bootstrap jquery function, below is the code in my controller, it's worked for me.
Html
<input class="form-control" name="date" id="datetimepicker" placeholder="select date">
Controller
$(function() {
//for displaying datepicker
$('#datetimepicker').datetimepicker({
viewMode: 'years',
format: 'DD/MM/YYYY',
});
//for getting input value
$("#datetimepicker").on("dp.change", function() {
$scope.selecteddate = $("#datetimepicker").val();
alert("selected date is " + $scope.selecteddate);
});
});
I just found a solution to this myself. I just pass in the model name to the directive (which I found most of online). This will set the value of the model when the date changes.
<input data-ng-model="datepickertext" type="text" date-picker="datepickertext" />{{datepickertext}}
angular.module('app').directive('datePicker', function() {
var link = function(scope, element, attrs) {
var modelName = attrs['datePicker'];
$(element).datepicker(
{
onSelect: function(dateText) {
scope[modelName] = dateText;
scope.$apply();
}
});
};
return {
require: 'ngModel',
restrict: 'A',
link: link
}
});
I am using Angular JS 1.5.0 and Bootstrap 3 Datetimepicker from https://eonasdan.github.io/bootstrap-datetimepicker/
After some time and struggling, I finally found a solution how to make it work for me :)
JSFiddle: http://jsfiddle.net/aortega/k6ke9n2c/
HTML Code:
<div class="form-group col-sm-4" >
<label for="birthdate" class="col-sm-4">Birthday</label>
<div class="col-sm-8">
<div class="input-group date" id="birthdate" ng-model="vm.Birthdate" date-picker>
<input type="text" class="form-control netto-input" ng-model="vm.Birthdate" date-picker-input>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<div class="form-group col-sm-4">
<label for="birthdateText" class="col-sm-4">Model:</label>
<div class="col-sm-8">
<input type="text" class="form-control netto-input" ng-model="vm.Birthdate">
</div>
</div>
</body>
App.js:
Simply a controller setting the viewmodels Birtdate attribute:
var app = angular.module('exampleApp',[]);
app.controller('ExampleCtrl', ['$scope', function($scope) {
var vm = this;
vm.Birthdate = "1988-04-21T18:25:43-05:00";
}]);
The first directive is initializing the datetimepicker and listening to the dp.change event.
When changed - the ngModel is updated as well.
// DatePicker -> NgModel
app.directive('datePicker', function () {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
$(element).datetimepicker({
locale: 'DE',
format: 'DD.MM.YYYY',
parseInputDate: function (data) {
if (data instanceof Date) {
return moment(data);
} else {
return moment(new Date(data));
}
},
maxDate: new Date()
});
$(element).on("dp.change", function (e) {
ngModel.$viewValue = e.date;
ngModel.$commitViewValue();
});
}
};
});
The second directive is watching the ngModel, and triggering the input onChange event when changed. This will also update the datetimepicker view value.
// DatePicker Input NgModel->DatePicker
app.directive('datePickerInput', function() {
return {
require: 'ngModel',
link: function (scope, element, attr, ngModel) {
// Trigger the Input Change Event, so the Datepicker gets refreshed
scope.$watch(attr.ngModel, function (value) {
if (value) {
element.trigger("change");
}
});
}
};
});
I have the same problem and resolve like this
added in html part
<input class="someting" id="datepicker" type="text" placeholder="Dae" ng-model=""/>
and simple in Controller call
$scope.btnPost = function () {
var dateFromHTML = $('#datepicker').val();
Have you tried AngularUI bootstrap? It has all the bootstrap modules rewritten in angular(including datepicker): http://angular-ui.github.io/bootstrap/
One of ways around: set the id field to the input, then call document.getElementById('input_name').value to get the value of the input field.
Using this datepicker I had the same problem. I solved it using a little trick.
In the inputs tag I added a new attribute (dp-model):
<input class="form-control dp" type="text" dp-model="0" />
<input class="form-control dp" type="text" dp-model="1" />
...
And then in the js file I forced the binding in this way:
$scope.formData.dp = []; // arrays of yours datepicker models
$('.dp').datepicker().on('changeDate', function(ev){
$scope.formData.dp[$(ev.target).attr('dp-model')] = $(ev.target).val();
});
This worked for me:
set ng-model="date"
on your angular controller:
$scope.date = '';
$('#check-out').datepicker().on('changeDate', function (ev) {
$scope.date= $('#check-out').val();
$scope.$digest();
$scope.$watch('date', function (newValue, oldValue) {
$scope.date= newValue;
});
});
My actual trouble was that the value of the model was not reflected till another component on the scope was changed.
im using this , and my code :
this.todayNow = function () {
var rightNow = new Date();
return rightNow.toISOString().slice(0,10);
};
$scope.selecteddate = this.todayNow();
$(function() {
//for displaying datepicker
$('.date').datepicker({
format: 'yyyy-mm-dd',
language:'fa'
});
//for getting input value
$('.date').on("changeDate", function() {
$scope.selecteddate = $(".date").val();
});
});
To solve this, I have my HTML looking like this:
<div class='input-group date' id='datetimepicker1'>
<input type='text' ng-model="date.arrival" name="arrival" id="arrival"
class="form-control" required />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
My ng-model wouldn't update, so I used this to fix it:
$("#datetimepicker1").on("dp.change", function (e) {
$scope.date.arrival = $("#arrival").val(); // pure magic
$('#datetimepicker2').data("DateTimePicker").minDate(e.date); // this line is not relevant for this example
});

Filter events by date using AngularJS

I'd like to filter my events by date using AngularJS and jQuery Datepicker. But it seems not working using the input text...
My code below works with the title of the event...
Main issue : when I choose a date in the jQuery Datepicker, I get no events...
$scope.onChange = function(){
$scope.search.start = new Date(moment($scope.search.start, 'DD/MM/YYYY').format("YYYY-MM-DD 00:00:00"));
$scope.search = _.reduce($scope.search, function(result, n, key) {
if (n !== '') {
result[key] = n;
}
return result;
}, {});
$scope.events = data.events;
$scope.filteredEvents = $filter('filter')($scope.events, $scope.search);
};
<input class="form-control datepicker" id="start" type="text" ng-model="search.start" ng-change="onChange()" autocomplete="off">
<input class="form-control" id="title" type="text" ng-model="search.title" ng-change="onChange()" autocomplete="off">
<div class="col-12" ng-repeat="event in events | filter:search | limitTo:limit">
<div class="event">
<div class="event-body">
{{event.title}}
<p ng-if="event.end" class="event-date">{{event.start | date:"dd/MM/yyyy"}}</p>
</div>
</div>
<div class="space10"></div>
</div>
Out of the Box the JQueryUI datepicker will not work with angular because the angular model is not being updated when Jquery sets the input value. You need to tell JQueryUI datepicker to update the model.
The best way to do that is create a directive that sets up the datepicker and adds an onSelect method that sets the view value model controllers view value ngModelCtrl.$setViewValue and applies it to the scope scope.$apply()
HTML:
<input type="text" ng-model="date" datepicker />
JS
app.directive('datepicker', function() {
return {
restrict: 'A',
require : 'ngModel',
link : function (scope, element, attrs, ngModelCtrl) {
$(function(){
element.datepicker({
dateFormat:'dd/mm/y',
onSelect:function (date) {
scope.$apply(function () {
ngModelCtrl.$setViewValue(date);
});
}
});
});
}
}
});
If you also want to use a date in a different format than available using the Date Picker you will need to format using the ng-change directive.
<input class="form-control " datepicker type="text"
ng-model="search.start" ng-change="onChange()"/>
$scope.onChange = function(){
$scope.search.start = moment($scope.search.start, 'DD/MM/YYYY')
.format("YYYY-MM-DDT00:00:00.000[Z]");
};
Working Example

binding the model value of Twitter Bootstrap datepicker in controller in Angular js

I am building an application with Angular.js and Twitter Bootstrap.
HTML:
<div ng-controller="myController">
<label for="event">Event</label>
<input type="text" ng-model="event"/>
<label for="eventdate">Date</label>
<div class="input-append date" id="datepicker" data-date-format="dd-mm-yyyy">
<input class="span2" size="20" type="text" id="datepicker" ng-model="eventdate" required>
<span class="add-on"><i class="icon-th"></i></span>
<input class="btn-primary" type="submit" id="submit" value="Submit" ng-click="submit()" />
</div>
Controller:
var myApp1 = angular.module('myApp1', []);
myApp1.controller('myController', function($scope) {
$('#datepicker').datepicker();
$scope.submit = function () {
console.log($scope.event);
console.log($scope.eventdate);
};
});
When I click "Submit" button,
console.log($scope.event); prints the data entered in event text box.
But console.log($scope.eventdate); prints "undefined" when I select a date from the date picker
What may be the reason?
Please advice.
Your bootstrap datepicker is a 3rd-component outside of angular. When you change your date from the datepicker, angular is not aware of the changes.
You have to write a custom directive like this:
app.directive('datepicker', function() {
return {
restrict: 'A',
// Always use along with an ng-model
require: '?ngModel',
link: function(scope, element, attrs, ngModel) {
if (!ngModel) return;
ngModel.$render = function() { //This will update the view with your model in case your model is changed by another code.
element.datepicker('update', ngModel.$viewValue || '');
};
element.datepicker().on("changeDate",function(event){
scope.$apply(function() {
ngModel.$setViewValue(event.date);//This will update the model property bound to your ng-model whenever the datepicker's date changes.
});
});
}
};
});
Apply the directive to html:
<div class="input-append date" datepicker ng-model="eventdate" data-date-format="dd-mm-yyyy">
<input class="span2" size="20" type="text" required="" />
<span class="add-on">
<i class="icon-th"></i>
</span>
</div>
DEMO

Resources