Transforming Bootstrap datepicker directive to AngularJS Material - angularjs

I have beginner knowledge of angularjs and am having problems transforming a Datepicker directive that uses a Bootstrap template to AngularJS Material. The original Bootstrap template looks like this:
template:
'<div ng-class="{\'input-group\': !snDisabled, \'has-error\': isInvalid}" style="width: 100%;">' +
'<input type="text" name="{{field.name}}" class="form-control" placeholder="{{field.placeholder}}" ng-model="formattedDate" ng-model-options="{updateOn: \'blur\', getterSetter: true}" ng-disabled="snDisabled" />' +
'<span class="input-group-btn" ng-hide="snDisabled">' +
'<input type="hidden" class="datepickerinput" ng-model="formattedDate" ng-readonly="true" />' +
'<button class="btn btn-default" type="button">' +
'<glyph sn-char="calendar" />' +
'</button>' +
'</span>' +
'</div>',
However, I've updated it to AJS Material:
template: '<div ng-class="{\'input-group\': !snDisabled, \'has-error\': isInvalid}" style="width: 100%;">' +
'<md-datepicker md-open-on-focus name="{{field.name}}" md-placeholder="{{field.placeholder}}" ng-model="formattedDate" ng-model-options="{updateOn: \'blur\', getterSetter: true}" ng-disabled="snDisabled"/>' +
'</div>',
When I load the page and check my console, I see an error "TypeError: Cannot read property 'setDate' of undefined"
Going back in the code, I realize that it calls upon an class element on the page input-group-btn, which doesn't appear in AngularJS Material:
link: function(scope, element, attrs, ngModel) {
var includeTime = scope.snIncludeTime;
var format;
format = includeTime ? dateTimeFormat.trim() : dateFormat.trim();
format = format.replace(/y/g, 'Y').replace(/d/g, 'D').replace(/a/g, 'A');
var dp = element.find('.input-group-btn').datetimepicker({
keepInvalid: true,
pickTime: scope.snIncludeTime === true,
format: "X"
}).on('dp.change', onDpChange);
function onDpChange(e) {
scope.formattedDate(e.date.format(format));
if (!scope.$root.$$phase)
scope.$apply();
}
function validate(formattedDate) {
scope.isInvalid = false;
if (formattedDate == null || formattedDate == '') {
dp.data('DateTimePicker').setValue(new Date());
return '';
}
if (isValidDate(formattedDate, format)) {
dp.data('DateTimePicker').setDate(moment(formattedDate, format));
} else if (isValidDate(formattedDate, moment.ISO_8601)) {
var date = moment.utc(formattedDate).clone().local();
dp.data('DateTimePicker').setDate(date);
formattedDate = date.format(format);
} else {
scope.isInvalid = true;
}
return formattedDate;
}
if (ngModel) {
ngModel.$parsers.push(validate);
ngModel.$render = function() {
validate(ngModel.$viewValue);
};
scope.formattedDate = function(formattedValue) {
if (angular.isDefined(formattedValue)) {
ngModel.$setViewValue(formattedValue);
if (scope.snChange) scope.snChange({
newValue: formattedValue
});
}
return ngModel.$viewValue;
};
} else {
scope.formattedDate = function(formattedValue) {
if (angular.isDefined(formattedValue)) {
scope.field.value = validate(formattedValue);
if (scope.snChange) scope.snChange({
newValue: formattedValue
});
}
return scope.field.value;
};
scope.$watch('field.value', function(newValue, oldValue) {
if (newValue != oldValue)
validate(newValue);
});
}
scope.$on('$destroy', function() {
dp.off('dp.change', onDpChange);
});
}
};
}
What do I have to modify in the code above in order for my AngularJS Material template to work without any errors?

you can use this jquery plugin instead of Bootstrap, it's very compatible with Angular
https://jqueryui.com/datepicker/
I recommend it as i always use it on my Angular projects.
Check out the documentation here and how to use:
http://api.jqueryui.com/datepicker/

Related

How to get value of chcecked checkbox - angularJS

J started learn Angular and I have trouble with getting value of checkboxes.
<label ng-repeat="role in groupsapp">
<input type="checkbox" ng-click="selectedRole([role.name,role.id,????])">{{role.name}}</label>
How get value checked/unchecked in place "???"
I found also:
ng-true-value="{{role.id}}_{{role.name}}_true"
ng-false-value="{{role.id}}_{{role.name}}_false"
but I don't know how to get this value of checkbox, anyone can help ?
to get it working with angular you need to add the ng-model directive to your input so angular will process it.
<label ng-repeat="role in groupsapp">
<input ng-model="role.value" type="checkbox" ng-click="selectedRole([role.name,role.id,role.value])">{{role.name}}
</label>
I guess you might have got your answer but still if in case in future if you want to use multiple check boxes and need to collect what all items are collected you can use a custom directive.Here is a link on how to use it.
Below is sample code snippet in HTML
<body ng-app="mainApp" ng-controller="MainCtrl">
<h1>Multi Check box</h1>
<multi-checkbox selectedlist="req.selectedList" orginallist="req.sourceList" value="code" label="desc" all="true" sort-by="desc"></multi-checkbox>
<pre ng-cloak>{{req.selectedList |json}}</pre>
</body>
This requires a source list(orginallist) and a destination list(selectedlist) where selected values should go,it also sorts the list as per your need.
Just add this directive in your JS file
mainApp.directive('multiCheckbox', ['$log', '$filter', '$timeout', function($log, $filter, $timeout) {
return {
restrict: 'EA',//E-element & A - attribute
template:
'<div> <div ng-show="checkbox.showAll" class="checkbox"> ' +
'<label style="font-size: 12px"> <input type="checkbox" ' +
'id="all" name="all" ng-model="checkbox.all" ' +
'ng-checked="checkbox.all" ng-change="selectAll()" /> All ' +
'</label> ' +
'</div>' +
'<div ng-repeat="item in list track by $index "class="checkbox"> ' +
'<label style="font-size: 12px"> <input type="checkbox" ' +
'id="{{item.value}}" name="{{item.label}}" ' +
'ng-checked="item.checked" ng-click="$parent.toggle($index)"/> {{item.label}}' +
'</label>' +
'</div> </div>',
replace: true, //to replace our custom template in place of tag <multi-checkbox>
transclude: false,//make it true if we want to insert anything btw directive tags
scope: { //isolate scope created
selectedlist: '=',
orginallist: '=',
value: '#',
label: '#',
all: '#',
sortBy: '#'
},
link: function($scope, element, attrs) {
$scope.checkbox = {};
$scope.checkbox.all = false; //set 'All' checkbox to false
$scope.checkbox.showAll = $scope.all == 'true' ? true : false;//to show/hide 'All' checkbox
//function called on click of check box
$scope.toggle = function(index) {
var item = $scope.list[index];
var i = $scope.selectedlist.length > 0 ? $scope.selectedlist.indexOf(item.value) : -1;
item.checked = !item.checked;
if (!item.checked) {
$scope.selectedlist.splice(i, 1);//remove item if unchecked
$scope.checkbox.all = false;//make 'All' to uncheck too
} else if (item.checked) {
$scope.selectedlist.push(item.value);//add item if checked
}
};
//function called when 'All' checkbox is checked
$scope.selectAll = function() {
var totalList = $scope.list;
$scope.selectedlist = [];
//if selected add all items
//if unchecked remove all items from selected list
angular.forEach(totalList, function(item) {
item.checked = $scope.checkbox.all;
if (item.checked) {
$scope.selectedlist.push(item.value);
} else {
$scope.selectedlist = [];
}
});
};
//always watch my source list if it has been modified and update back..
$scope.$watch('orginallist', function(value) {
//sort accordingly..
value = $filter('orderBy')(value, $scope.sortBy);
$scope.list = [];
if (angular.isArray(value)) {
angular.forEach(value, function(item) {
$scope.list.push({
value: item[$scope.value],
label: item[$scope.label],
checked: item.checked
});
});
}
}, true);
//clear 'All' checkbox value if all items are de selected
$scope.$watch('selectedlist', function(value) {
if (!angular.isArray(value) || (angular.isArray(value) && value.length <= 0)) {
$scope.checkbox.all = false;
}
}, true);
}
};
}]);

google-place autocomplete directive does not work when its used twice on a single page

I am using a google-place directive to fill the location. The directive I am using is mentioned below:
app.directive('googlePlaces', function(){
return {
restrict:'E',
replace:true,
scope: {location:'='},
template: '<input id="google_places_ac" name="google_places_ac" type="text" class="form-control area_input transition" />',
link: function($scope, elm, attrs){
if(attrs.city =='cities'){
var options = {
types: ['(cities)'],
componentRestrictions: {country: 'IN'}
}
}
else{
var options = {
componentRestrictions: {country: 'IN'}
}
}
var autocomplete = new google.maps.places.Autocomplete($("#google_places_ac")[0], options);
google.maps.event.addListener(autocomplete, 'place_changed', function() {
var place = autocomplete.getPlace();
if(attrs.city =='cities'){
$scope.location = $("#google_places_ac")[0].value + '"' +place.geometry.location.lat() + '"' + place.geometry.location.lng();
}
else{
$scope.location = place.geometry.location.lat() + ',' + place.geometry.location.lng();
}
$scope.$apply();
});
}
};
});
<google-places class="form-control location_field" location="location" city='cities' latlngs = "latlngs" ng-model="chosenPlace"></google-places>
But since I need to use it two times on a single page, the second directive does not work, i.e. it does not show autocomplete results.
Could anyone please help me fix this issue. Help would be much appreciated.
Thanks a lot for the reply.. I solved the issue, by replacing id(#google_places_ac) with 'elm[0]', where 'ele' is the element parameter passed in the link function, it refers to current element.

Angular Number Picker Directive Expression is undefined

I've read a few questions having to do with this topic and cannot figure out what I'm missing in my own direcitve.
angular.module('app')
.directive('numberPicker', [NumberPicker]);
function NumberPicker () {
var getTarget, getType;
getTarget = function (e) { return angular.element(e.target); }
getType = function (e) { return getTarget(e).attr('direction-type'); }
return {
restrict: 'E',
replace: true,
require: 'ngModel',
scope: {
value: '='
},
template: '<div class="ui action input">' +
'<input value="{{value}}" type="text" />' +
'<button class="ui icon button" type="button" direction-type="up" ng-class="{disabled : canUp === false}">' +
'<i class="angle up icon" direction-type="up"></i>' +
'</button>' +
'<button class="ui icon button" type="button" direction-type="down" ng-class="{disabled : canDown === false}">' +
'<i class="angle down icon" direction-type="down"></i>' +
'</button>' +
'</div>',
controller: function ($scope) {},
link: function (scope, element, attrs, ctrl) {
scope.value = 0;
var options = {
min: 0,
max: 10,
step: 1
};
scope.$watch('value', function (newValue) {
scope.canDown = newValue > options.min;
scope.canUp = newValue < options.max;
if (ctrl.$viewValue != newValue) {
ctrl.$setViewValue(newValue);
}
});
var changeNumber = function (event) {
var type = getType(event);
if ('up' === type) {
if (scope.value >= options.max) {
return;
}
scope.value += options.step;
}
if ('down' === type) {
if (scope.value <= options.min) {
return;
}
scope.value -= options.step;
}
}
var btn = element.find('button');
var input = element.find('input');
btn.on('click', function (e) {
scope.$apply(function () {
changeNumber(e);
});
e.preventDefault();
});
input.on('change', function (e) {
scope.value = input[0].value;
scope.$apply();
})
scope.$on('$destroy', function () {
btn.off('touchstart touchend click')
});
}
}
}
The purpose of this was to create a number picker form element for Semantic UI. It was working perfectly a few days ago. And this error is so vague I can't even process where to start. Did I mention I am an Angular noob?
The error is :
Error: [$compile:nonassign] Expression 'undefined' used with directive 'numberPicker' is non-assignable!
How do you use the directive?
According to the definition you need to have both attributes "value" and "ng-model" set.
For example:
<number-picker value="xyz" ng-model="abc"></number-picker>
The error "Expression 'undefined' used with directive..." is normally thrown if one of the scope values is not set.

Making AngularUI datepicker an actual directive

I'm trying to turn angular-ui's date-picker into an actual directive.
I can display it on the page with the correct model and format, but the toggling doesn't work for me (trying to name it via the attributes).
Here is my code:
My html
<date-picker data-format="dd/MM/yyyy" data-model-name="dateReviewed" data-ng-model="oneWMS.dateReviewed" data-status="statusDateReviewed" data-opened="openDateReviewed"></date-picker>
My directive
function datePicker() {
return {
restrict: 'AE',
require: 'ngModel',
scope: {
format: '#',
mod: '=ngModel',
status: '#',
opened: '#'
},
template: '<div class="input-group">' +
'<input type="text" class="form-control" datepicker-popup="{{format}}" data-ng-model="mod" is-open="status.opened" ng-required="true" close-text="Close" />' +
'<span class="input-group-btn">' +
'<button type="button" class="btn btn-default" ng-click="opened($event)"><i class="glyphicon glyphicon-calendar"></i> </button>' +
'</span>' +
'</div>',
link: function(scope, iElement, iAttrs) {
// all the directive code
console.log(iAttrs.format); // dd/MM/yyyy
console.log(iAttrs.ngModel); // oneWMS.dateReviewed
console.log(iAttrs.status); // statusDateReviewed
console.log(iAttrs.opened); // openDateReviewed
console.log(iAttrs.modelName); // dateReviewed
var modelStatusDate = iAttrs.status;
var modelOpenDate = iAttrs.opened;
var modelName = iAttrs.modelName;
scope.today = function() {
scope.modelName = new Date();
scope.dateApproved = new Date();
scope.today();
scope.clear = function () {
scope.modelName = null;
};
scope.modelOpenDate = function($event) {
scope.modelStatusDate.opened = true;
};
scope.modelStatusDate = {
opened: false
};
scope.getDayClass = function(date, mode) {
if (mode === 'day') {
var dayToCheck = new Date(date).setHours(0,0,0,0);
for (var i=0;i<scope.events.length;i++){
var currentDay = new Date(scope.events[i].date).setHours(0,0,0,0);
if (dayToCheck === currentDay) {
return scope.events[i].status;
}
}
}
return '';
};
} // link
} // return
} // picker
}
Don't know if just opening the picker doesn't work or if the date selecting will fail as well.
I have made a custom date directive using angularui date-picker. Have a look, it might be useful to you. Here is plunkr url . http://plnkr.co/edit/FDigEjyMYm5SVYnQyZGp.
If you're using Angular and Bootstrap, I would strongly encourage you to use Angular UI Bootstrap.
The other Angular UI libraries are not actively maintained.

AngularJS Checkbox and $validators

I have a list of checkboxes that are backed by a model that is an array of ids.
<input type="checkbox" name="checkers" value="black" ng-model="board" />
<input type="checkbox" name="checkers" value="white" ng-model="board" />
the model would look like:
[ 'black', 'white' ]
so there is a number of 'hacks' to get this to work like one would think and even a directive checklist-model.
My problem is I have a directive that does dynamic validation using ngModelController's $validators. That directive looks something like this:
module.directive('validator', function($parse) {
return {
restrict: 'A',
require: '?ngModel',
link: function($scope, $element, $attrs, ngModelCtrl) {
var rules = $parse($attrs.validator)($scope);
ngModelCtrl.$validators.myValidator = function(val){
// this is simplified, real case is much more complex
if(rules.minSelections > 0){
return !(val.length <= rules.minSelections);
}
if(rules.required){
return !val.length;
}
}
}
}
});
I attached it to my checkboxes like:
<input type="checkbox" name="checkers" val="black" validators="{ minSelections: 1 }" ng-model="board" />
<input type="checkbox" name="checkers" val="white" validators="{ minSelections: 1 }" ng-model="board" />
problem is the val in the myValidator validation always returns true/false. I can't ever seem to get ahold of the 'actual' model I need despite several different approaches and even using that directive. On a note: the $validators runs BEFORE the click on that directive.
Does anyone have any suggestions?
I ended up creating my own checkbox directive and manually triggering validation to happen.
If you take a look below, you can see how I watch the collection and if the value has changed I commit the value and re-trigger the validation manually.
Heres the code for others:
define(['angular'], function (angular) {
// Use to style checkboxes, bind checkboxes to arrays, and run validators on checkboxes
// Modified from: https://github.com/bkuhl/angular-form-ui/tree/master/src/directives/checkBox
var module = angular.module('components.checkbox', []);
/**
* <check-box ng-model="isChecked()"></check-box>
* Required attribute: ng-model="[expression]"
* Optional attribute: value="[expression]"
*/
module.directive('checkBox', function () {
return {
replace: true,
restrict: 'E',
scope: {
'externalValue': '=ngModel',
'value': '&'
},
require: 'ngModel',
template: function (el, attrs) {
var html = '<div class="ngCheckBox' + ((angular.isDefined(attrs.class)) ? ' class="'+attrs.class+'"' : '') + '">'+
'<span ng-class="{checked: isChecked}">' +
'<input type="checkbox" ng-model="isChecked"' + ((angular.isDefined(attrs.id)) ? ' id="'+attrs.id+'"' : '') + '' + ((angular.isDefined(attrs.name)) ? ' name="'+attrs.name+'"' : '') + '' + ((angular.isDefined(attrs.required)) ? ' name="'+attrs.required+'"' : '') + '/>'+
'</span>'+
'</div>';
return html;
},
controller: function ($scope) {
if (angular.isArray($scope.externalValue)) {
$scope.isChecked = $scope.externalValue.indexOf($scope.value()) >= 0;
} else {
$scope.isChecked = !!$scope.externalValue;
}
$scope.$watch('isChecked', function (newValue, oldValue) {
if (angular.isDefined(newValue) && angular.isDefined(oldValue)) {
//add or remove items if this is an array
if (angular.isArray($scope.externalValue)) {
var index = $scope.externalValue.indexOf($scope.value());
if(newValue) {
if( index < 0 ) $scope.externalValue.push($scope.value());
} else {
if( index >= 0 ) $scope.externalValue.splice(index, 1);
}
} else {
//simple boolean value
$scope.externalValue = newValue;
}
}
});
},
link: function ($scope, $elm, $attrs, ngModel) {
$scope.$watchCollection('externalValue', function(newVal) {
if (newVal.length) {
ngModel.$setTouched();
ngModel.$commitViewValue();
ngModel.$validate();
}
});
}
};
});
return module;
});

Resources