Customizing bootstrap angularjs datepicker - angularjs

We are using bootstrap datepicker for our project needs. What we need to do is that whenever user selects the today's date, date has to be shown appended with "(TODAY)" like "May 12, 2008 (TODAY)" in the textbox.
What can be the best approach here? As we are using this datepicker at multiple places, I think having a general approach like creating a directive would be helpful. Was trying to bind change event with the datepickerPopup directive, but have not been able to make it work.
Here is what I have tried so far:
Have created one decorator. This seems to be working. However one issue, how do I access the parent directive methods in this decorator (such as dateFilter, parseDate here)? (sorry if you find my questions naïve, as I am very new to angularjs). I have attached the code.
app.config(function($provide) {
$provide.decorator('datepickerPopupDirective', function($delegate) {
var directive = $delegate[0],
link = directive.link;
//closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
//angular.extend(directive.scope, { 'monthChanged': '&' });
directive.compile = function() {
return function(scope, element, attrs, ngModel) {
link.apply(this, arguments);
ngModel.$render = function() {
var date = ngModel.$viewValue ? dateFilter.apply(ngModel.$viewValue, dateFormat) : '';
var currentDate = new Date();
currentDate.setHours(0,0,0,0);
if(date.getTime() == currentDate.getTime()){
element.val(date + "(TODAY)");
}else{
ngModel.$setViewValue(scope.date);
}
scope.date = parseDate( ngModel.$modelValue );
};
};
};
return $delegate;
});
});

You've got a couple of options. The quick and dirty way would be to extract the component from the third party library into your own directive and template, and modify as needed. The disadvantage to this is that you'll no longer be up to date with the component. Future versions would require you to manually update your directive, which you might not care about... yet.
The second option, is to take advantage of angular's $provide.decorator
This post gives you an initial idea
What are "decorators" and how are they used?
Here's a basic example of decorating a directive definition object
app.directive("foo", function() {
return {
replace: true,
template: '<div>This is foo directive</div>'
};
});
app.config(function($provide) {
$provide.decorator('fooDirective', function($delegate) {
var directive = $delegate[0];
directive.restrict = "AM";
return $delegate;
});
});
In your case, you'll want to override what value is being referenced on the template. You could decorate the entire directive if you want to completely modify it.
I would recommend this as the best approach when looking to tackle modification of a third party library.
Here's an example of decorating a directive to override a scope level function, and use an existing scope variable within the directive while overriding it.
https://jsfiddle.net/wvty8rpc/3/

Related

AngularJS. How to pass model to the custom xeditable directive? (I'm trying to find the best way for updating model from ui-select)

I have directive, that allow to integrate ui.select with xeditable. This is my directive:
var Dashboard = angular.module('Dashboard');
Dashboard.directive('editableUiSelect', ['editableDirectiveFactory', 'editableNgOptionsParser',
function(editableDirectiveFactory, editableNgOptionsParser) {
return editableDirectiveFactory({
directiveName: 'editableUiSelect',
inputTpl: '<span></span>',
scope: {
eNgModel: "="
},
render: function() {
this.parent.render.call(this);
var parsed = editableNgOptionsParser(this.attrs.eNgOptions);
var filter = " | filter: $select.search";
var html = "<ui-select ng-model='eNgModel' theme='bootstrap' style='width: 150px'>"+
"<ui-select-match><span ng-bind='$select.selected.name'></span></ui-select-match>"+
"<ui-select-choices repeat='"+parsed.ngRepeat+filter+"'>"+"<span ng-bind='"+parsed.locals.displayFn+"'></span>"+
"</ui-select-choices></ui-select>";
this.inputEl.removeAttr('ng-model');
this.inputEl.removeAttr('ng-options');
this.inputEl.html(html);
}
});
}]);
I want to pass model to my directive dynamically, but it seems, that xeditable editableDirectiveFactory not allow me to do this. As result, I can't save value, that I selected from this ui-select. What can I do for fixing this issue?
SOLVED(see my answer below), but my approach is just ugly workaround. I need somebody who can show me the best way of solving this problem.
Currently I solved this problem by adding setModel method to on-select attribute of ui-select.
So, this way my html variable changed to:
var html = "<ui-select ng-model='"+this.name+"' theme='bootstrap' style='width: 150px' on-select='setModel($item)'>"+
"<ui-select-match><span ng-bind='$select.selected.name'></span></ui-select-match>"+
"<ui-select-choices repeat='"+parsed.ngRepeat+"'>"+"<span ng-bind='"+parsed.locals.displayFn+"'></span>"+
"</ui-select-choices></ui-select>";
Next, I just add to each controller setModel method. (I mean controllers, in which template I'm using this directive). Example of method:
$scope.setModel = function(item) {
if (item.hasOwnProperty('property')) {
$scope.object1 = item;
} else {
$scope.object2 = item;
}
}
And, finally, I just getting $scope.objectX value in my save method.
This approach is just ugly workaround, so I really need somebody, who can show me the best way.

Disable spellcheck attribute with AngularJS

Instead of going into all of the views in an AngularJS application, is there an easier way to add the spellcheck="false" attribute dynamically to all forms in an AngularJS application?
Once added, every form in the application would be like this:
<form spellcheck="false">
Angular actually overrides the HTML FORM element as a directive.
https://github.com/angular/angular.js/blob/master/src/ng/directive/form.js
You can decorate this directive in your app configuration like so:
angular.module('app', [])
.config(['$provide', function ($provide) {
$provide.decorator('formDirective', ['$delegate', function ($delegate) {
var formDirective = $delegate[0];
var oldCompile = formDirective.compile;
formDirective.compile = function (tElement, tAttrs, transclude) {
// get original links
var compile = oldCompile ? oldCompile.apply(this, arguments) : {};
tElement.attr("spellcheck", "false");
return compile;
};
return $delegate;
}]);
}]);
Why don't you just add it with jQuery after the DOM loads?
$(document).ready(function () {
// ... cool onLoad stuff
// Set form spellcheck attributes to false
$('form').attr('spellcheck', false);
});
setAttribute supported by all mainstream browsers.
So the simple way is just as follows(no jQuery needed):
angular.element(document).ready(() => {
// type of attribute value should be String
document.body.setAttribute('spellcheck', 'false');
});
the most simple answer is to add the attribute spellcheck="false" directly to your body element(WHY NOT?).

Is it possible to set ng-view to replace: true?

I'm integrating Angular into a web application and I noticed that the wrapper is messing with some of the CSS on the page. After researching into directives, I saw that custom directives can have a property called 'replace' to be set to true so that the templateUrl directly replaces instead of being wrapped in the directive's tags.
Is there a way to do the same with ng-view, or more generally any Angular directive? Thanks for the help!
I think your best bet is to decorate the original ng-view directive and add the replace:true switch to it. Note, replace is going to be - hah - replaced in the next major version of angularjs, but for now, this will work:
app.config(function($provide) {
$provide.decorator('ngViewDirective', function($delegate) {
var directive = $delegate[0];
directive.replace = true;
return $delegate;
});
});
And of course, the jsFiddle: Here
Is there a way to do the same with ng-view
I can't think of a way of doing that without editing the source of Angular or something. replace is for custom directives, not (necessarily) the built in ones.
or more generally any Angular directive? Thanks for the help!
Same thing. But you wouldn't want to do it with any directive, seeing how you could have multiple directives in a single element. I suppose you may want to do it with all template'd directives (since I think you can only have one per element), but, again, without editing the source of the directive I can't think of a way to do that.
Also, note that Angular has stated that replace "will be removed in next major release", so it's probably not best to rely on it.
You can, however, if you wish, create you own ngInclude/ngView-esque directive that renders templates without a wrapping tag.
A naive and probably problematic one (or at least inefficient) might look like:
app.directive('myRender', function ($compile, $http) {
return {
restrict: 'E',
link: function (scope, element, attrs) {
var newScope;
var newElement;
attrs.$observe('url', function (value) {
if (value) {
$http.get(value).then(function (result) {
if (newElement !== undefined) {
newElement.remove();
}
newElement = angular.element(result.data);
if (newScope !== undefined) {
newScope.$destroy();
}
newScope = scope.$new();
$compile(newElement)(newScope);
element.parent().append(newElement);
});
}
})
}
}
});
http://plnkr.co/edit/Vm96f2rQsT2PX1C3rgK8?p=preview
My first advice would be to adapt your css to angular if you want to use angular.
My other advice would to do your entire application in angular, and not add it after the fact.

Combining Polymer elements and angular

I have looked at a number of answers for binding Angular-JS scope data to Polymer components. (Use Angular-Bind-Polymer Another, And a third). It seems like this shouldn't be this hard, if the polymer components are truly just DOM. (Note that I'm using Chrome beta (36)).
I tried the Angular-Bind-Polymer suggestion, but no luck. My real interest is extending ngModel to work with Polymer so that I can use the Polymer check boxes, radio buttons, etc. For example, I tried getting paper-checkbox to work, so I tried the following, thinking that it should work:
var ngPaper = angular.module('ng-paper', []);
ngPaper.directive('paper-checkbox', function() {
console.log("Processing directive");
return {
restrict: 'E',
require: '?ngModel',
link: function(scope, element, attr, ctrl) {
console.log("Running linker");
element.on('click', function() {
scope.$apply(function() {
ctrl.$setViewValue(element[0].checked);
});
});
ctrl.$render = function() {
element[0].checked = ctrl.$viewValue;
};
ctrl.$isEmpty = function(value) {
return value != true;
};
ctrl.$formatters.push(function(value) {
return value === true;
});
ctrl.$parsers.push(function(value) {
return value ? true : false;
});
}
};
});
But no.
I then tried using angular-bind-polymer to bind the checked value on the paper-checkbox to a model attribute but didn't have any success.
I feel like if I could figure out how to get one of the form control elements to work, the others should fall quickly in line. Does anyone have a better idea on how to do this or an explanation as to why the directive I wrote isn't getting picked up and applied to the paper-checkbox?
I made this generic work-around that I use to watch changes on check-boxes and most Polymer elements from AngularJS, it's really useful while you find a more proper way, I hope it helps you.
You can also use it to manipulate Polymer Elements (E.g. Toggle).
in your HTML:
<paper-radio-group selected="firstOption">
<paper-radio-button label="First Option" id="firstOption" ng-click="change()"></paper-radio-button>
<paper-radio-button label="Second Option" id="secondOption" ng-click="change()"></paper-radio-button>
</paper-radio-group>
In the corresponding AngularJS controller, requites $scope.
var firstOption= document.querySelector('paper-radio-button[id="firstOption"]');
var secondOption= document.querySelector('paper-radio-button[id="secondOption"]');
console.log(firstOption);
console.log(secondOption);
$scope.change = function()
{
console.log(firstOption);
console.log(secondOption);
}
This way every time the user changes the selection, AngularJS will get notified so it can query the changes, you can scope the data you get back to something more specific, this is particularly useful to toggle Polymer Elements from AngularJS.
Let me know if this works for you, happy coding!

AngularJS: accessing the ngTouch service from a directive?

I really love how the new ng-click directive in Angular now automatically includes functionality for touch events. However, I am wondering if it is possible to access that touch-event service from my custom directive? I have lots of directives that require that I bind a click event to the given element, but I'm simply doing that using the typical jquery syntax (ex: element.on('click', function(){ ... })). Is there a way that I can bind an ng-click event to an element within a directive? Without having to manually put a ng-click tag on my element in the HTML of my view...?
I want to be able to harness the power of both click and touch events. I could obviously import a library (such as HammerJS or QuoJS) but I would prefer not to have to do that, especially since Angular is already doing it.
I can access the $swipe service and bind different elements to that, but is there a similar service for ngTouch?
For reference, this is an example of when I would want to do this:
mod.directive('datepicker', ['$timeout', function($timeout){
return {
link: function(scope, elem, attrs){
var picker = new DatePicker();
elem.on('click', function(e){
picker.show();
});
// I would rather do something like:
// elem.on('ngTouch', function(){ ... });
//
// or even:
// $ngTouch.bind(elem, {'click': ..., 'touch': ...});
}
}
}]);
UPDATE: As noted by below, the source code for the ng-click directive is here. Can anyone see a way to harness that code and turn it into a "bindable" service?
I don't think that's quite the right approach. I'd approach this by using a template within your directive and then using ngTouch within that.
mod.directive('datepicker', ['$timeout', function ($timeout) {
return {
template: '<div ng-touch="doSomethingUseful()"></div>',
link: function (scope, elem, attrs) {
var picker = new DatePicker();
scope.doSomethingUseful = function () {
// Your code.
}
}
}
}]);
UPDATE
Full example with additional attributes on the directive element:
http://codepen.io/ed_conolly/pen/qJDcr

Resources