I'm totally new to angular and I try to use angular-bootstrap-datetimepicker in my project. My html code is:
<span class="input-group-btn" ng-class="{open: openedDP}">
<button type="button" class="btn btn-default btn-sm" ng-click="open()">
<i class="glyphicon glyphicon-calendar"></i>
</button>
<ul class="dropdown-menu" role="menu">
<datetimepicker ng-model="abc"
on-set-time="close(new, old)">
</datetimepicker>
</ul>
</span>
<input id="abc" ng-model="abc" class="form-control" date-time-input="DD-MM-YYYY HH:mm:SS" />
I wanted to close a calendar when user clicks anywhere outside it. I almost copy-pasted the code from ui.bootstrap. Original one is inside directive and looks like this:
var documentClickBind = function(event) {
if (scope.isOpen && event.target !== element[0]) {
scope.$apply(function() {
scope.isOpen = false;
});
}
};
scope.$watch('isOpen', function(value) {
if (value) {
scope.$broadcast('datepicker.focus');
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
scope.position.top = scope.position.top + element.prop('offsetHeight');
$document.bind('click', documentClickBind);
} else {
$document.unbind('click', documentClickBind);
}
});
My version (inside controller):
var documentClickBind = function (event) {
if ($scope.openedDP) {
$scope.$apply(function () {
$scope.openedDP = false;
});
}
};
$scope.$watch('openedDP', function (value) {
if (value) {
$timeout(function() {
$document.bind('click', documentClickBind);
}, 0, false);
} else {
$document.unbind('click', documentClickBind);
}
});
I removed "element" variable because I don't have it in my controller and it seems to work, but I don't know why. Maybe it works just by chance? Why clicking inside calendar is different than clicking anywhere else? In addition I'd like to avoid creating multiple functions like this when I have multiple datepickers on a page.
The behavior you want is built-in to the bootstrap dropdown, if you have bootstrap already in your project you might consider making use of its dropdown.
If not, you could create a custom directive on the page that broadcasts a 'page-clicked' event when the user clicks on another part of the page, then each controller can listen for that event on their scope and react accordingly.
Related
I am using the materializecss Datepicker (https://materializecss.com/pickers.html), and this seems like it should be really straightforward so I'm losing my mind a bit over it. Put simply, I'm trying to trigger an event only if the "Ok" button is clicked, but cannot identify that in the onClose() function provided. If I try to listen for the specific button click, I lose all that comes with the onClose() function (which nicely packages up all the info I need on that event).
Is there any way, with the onClose() function, that I can identify which button caused that onClose() to fire?
I'm admittedly a novice when it comes to javascript and jquery, so any help is appreciated.
HTML
<input type="text" class="datepicker" value="8/4/2018" job="533">
Javascript Code to initialize the datepicker
$(document).ready(function(){
$('.datepicker').datepicker({
"format": "m/d/yyyy",
onClose() {
// only do something if this was fired from the "Done" button
}
})
});
Datepicker modal created
<div class="modal datepicker-modal" id="modal-a5a43c91-2426-5565-c216-1d8ccd0cfc1d" tabindex="0">
<div class="modal-content datepicker-container">
<div class="datepicker-date-display">
<span class="year-text">
</span>
<span class="date-text">
</span>
</div>
<div class="datepicker-calendar-container">
<div class="datepicker-calendar">
</div>
<div class="datepicker-footer">
<button class="btn-flat datepicker-clear waves-effect" style="visibility: hidden;" type="button">
</button>
<div class="confirmation-btns">
<button class="btn-flat datepicker-cancel waves-effect" type="button">
Cancel
</button>
<button class="btn-flat datepicker-done waves-effect" type="button">
Ok
</button>
</div>
</div>
</div>
</div>
</div>
This code below can help you get the right button you want to do some stuff with it. Add 2 listeners to the done and cancel buttons of that opened modal right in onOpen(), and remove listeners onClose().
This will work for you.
<script>
$(document).ready(function() {
function Cancel() {
if(!this.hasEvent) {
this.hasEvent = true;
console.log('Clicked on cancel btn:', this.cancelBtn);
}
}
function Done() {
if(!this.hasEvent) {
this.hasEvent = true;
console.log('Clicked on done btn:', this.doneBtn);
}
}
$('.datepicker').datepicker({
"format": "m/d/yyyy",
onOpen: function(e) {
var that = this;
that.hasEvent = false;
this.cancelBtn.addEventListener('click', Cancel.bind(that))
this.doneBtn.addEventListener('click', Done.bind(that))
},
onClose: function(e) {
var that = this;
this.cancelBtn.removeEventListener('click', Cancel.bind(that))
this.doneBtn.removeEventListener('click', Done.bind(that))
}
})
});
</script>
First you can initialize the Datepicker just by using vanillaJS, then if you check materialize.css file then you'll find the class name of Ok button, that is, .datepicker-done. You can attach addEventListener to this button and call any function you want.
document.addEventListener('DOMContentLoaded', function () {
var elems = document.querySelector('.datepicker');
var instance = M.Datepicker.init(elems);
var doneBtn = document.querySelector('.datepicker-done');
doneBtn.addEventListener('click', callThis); //Attaching a 'click' event to done button
function callThis() {
console.log('Done Button clicked only!'); //Checking the done button click
console.log(elems.getAttribute('job')); //To get the job attribute value
}
});
All of the responses were extremely helpful in figuring this out, thank you to those who contributed!
I don't know if this is the most elegant solution, but it is working for me. I kept the jquery call to initialize all of the datepickers on the page, then added the event listeners with a forEach loop. To get the values I wanted, I had to do that crazy looking series of .parent() calls, but it consistently gets me the right information. Hopefully this helps someone dealing with the same issue in the future (or it can help someone else provide a more effective answer!).
$(document).ready(function(){
$('.datepicker').datepicker({
"format": "m/d/yyyy",
})
});
document.addEventListener('DOMContentLoaded', function () {
var doneBtn = document.querySelectorAll('.datepicker-done');
doneBtn.forEach(function(elem) {
elem.addEventListener('click', callThis)
});
});
function callThis() {
var update = $(this).parent().parent().parent().parent().parent().siblings('input');
// do the thing I want with that input value
};
I have a button which on click will save the form information.
The problem is, user instead of clicking 1's on the "Save" button clicks on it multiple times as long as it disappears on the screen. With this, I am saving same form which inturn throw duplicate exceptions.
Please help me. Thanks in advance.
<button ng-click="myFunc()">Save</button>
In above code, myFunc() is triggered with the number of times user clicks.
Use a $scope variable and ng-disabled to update the click and check for the variable,
<button ng-click="myFunc()" ng-disabled="buttonClicked"></button>
Controller
$scope.buttonClicked = true;
I think disable the button after the first click will help you to solve this issue.
That's in case you want this feature related to multiple buttons:
JS:
$scope.disabled = {};
$scope.myFunc = function(identifier) {
$scope.disabled[identifier] = 1;
...
}
HTML:
<button ng-click="myFunc(identifier)" ng-disabled="disabled.identifier"></button>
In case that you want this feature only on one button:
JS:
$scope.disabled = 0;
$scope.myFunc = function() {
$scope.disabled = 1;
...
}
HTML
<button ng-click="myFunc()" ng-disabled="disabled"></button>
You can disable your submit button after the first click to prevent duplicate entry.
For Example, you have HTML something like,
<div ng-app="mydemo" ng-controller="myController">
<button type="submit" ng-disabled="isDisabled" ng-click="myFunc()"> Submit</button>
</div>
angular.module('mydemo', [])
.controller('myController',function($scope){
$scope.isDisabled = false;
$scope.disableButton = function() {
$scope.isDisabled = true; // To disable Button
}
});
This way you can disable button. It will surely work for you. Thanks.
create a directive for this, so that it can be reused
app.directive('clickAndDisable', function() {
return {
scope: {
clickAndDisable: '&'
},
link: function(scope, iElement, iAttrs) {
iElement.bind('click', function() {
iElement.prop('disabled',true);
scope.clickAndDisable().finally(function() {
iElement.prop('disabled',false);
})
});
}
};
});
so use click-and-disable="myFunc()" rather than ng-click
Hiding the form after first click resolved this issue.
Problem is that ng-click works on so event if cancelTicket === false it still fires ng-click. How can I stop that?
<div class="btn-block save-changes padding-10" ng-class="{'gray':cancelTicket===false,'secondary-button':cancelTicket===true}" ng-click="CancelTicket(ticketPin)" ng-disabled="cancelTicket===false" style="display: table;">
<div class="button-container padding3" ng-class="{'pointer':cancelTicket===true}">
<button-spinner promise="cancelPromise"></button-spinner>
<div style="display: inline-block !important;"> #Translator.Translate("CANCEL") </div>
</div>
</div>
Event is triggered even if the div is disabled.
You can avoid this by using lazy evaluation of expressions like isDisabled || action() so action would not be called if isDisabled is true.
In your case it will be:
ng-click="cancelTicket === false || CancelTicket(ticketPin)"
You should change DIV tag to Button Tag.
It works for me.
You can disable click events when an element with ng-click is disabled.
jQuery:
$('*[ng-click]').on('click',function(event) {
var $el = $(event.target);
if($el.attr('disabled')) {
event.stopPropagation();
}
});
Doing this on all DOM elements could produce unwanted results. Also, you will need to run the above on any new HTML updated on the DOM.
Instead, we can modify just buttons to work as expected.
Angular:
angular.module('app').directive('button',function() {
return {
restrict: 'E',
link: function(scope,el) {
var $el = angular.element(el);
$el.bind('click', function(event) {
if($el.attr('disabled')) {
event.stopImmediatePropagation();
}
});
}
}
});
I would not do the above on div elements as it would be to heavy. Instead, modify your approach so that button elements are only used for clickable interactions. You can then style them to look like other div elements.
<a class="btn btn-danger btn-xs" ng-click="vm.handleRemove(device)" ng-disabled="status === 1">Delete</a>
Change a tag to button tag then OK
<button class="btn btn-danger btn-xs" ng-click="vm.handleRemove(device)" ng-disabled="status === 1">Delete</button>
My solution has been to use an html directive with an attribute used instead of ng-click, so that
<html-tag-directive new-click="ctrl.functionToCall()" disabled="ctrl.disabled" >
and the directive defined as follow:
1) template:
<button type="button"
ng-disabled="disabled" ng-click="onClick()">
</button>
2) controller:
angular.module('module')
.directive('htmlTagDirective',function() {
return {
restrict:'E',
templateUrl:'template.html',
scope:{
disabled:'=',
click: '&'
},
link:function(scope,element){
scope.onClick = function() {
if (!(scope.disabled)) {
scope.newClick();
}
};
}
};
});
I'm using angularjs with the implementation "Angular Material" (here: https://material.angularjs.org/latest/#/)
I'm using the SideNav component: https://material.angularjs.org/latest/#/demo/material.components.sidenav to open a lateral container on click in a menu link. It's working right now... But when i click the menu item i should change the value of a boolean variable from false to true.. This doesn't happen. I don't know exactly why. In my controller i have this part of code:
$scope.toggleRight = buildToggler('right');
$scope.myBoolean = false;
/**
* Build handler to open/close a SideNav; when animation finishes
* report completion in console
*/
function buildToggler(navID) {
var debounceFn = $mdUtil.debounce(function(){
$mdSidenav(navID)
.toggle()
.then(function () {
$log.debug("toggle " + navID + " is done");
});
},300);
return debounceFn;
};
$scope.close = function () {
$mdSidenav('right').close()
.then(function () {
$log.debug("close RIGHT is done");
});
};
That it's the part to open and close the container. Then in my html
<li>
<a data-ng-click="myBoolean = true;">
Open the container
</a>
</li>
So, if the variable was "false" now should be "true" when the item of the menu is clicked. But still remains false.. What's wrong?
//Just write down the below function i controller
$scope.openContainer = function () {
$scope.myBoolean = true;
}
//and in View change below
<li>
<a data-ng-click="openContainer()">Open the container</a>
</li>
Try changing the variable in this way:
<li>
<a data-ng-click="$parent.myBoolean = true;">
Open the container
</a>
</li>
Use $parent property. I believe the sidenav component has its own scope and the link is inside the sidenav component. So in order to access the variable in your scope you need to go one level upper (in the parent scope)
I have an input button that I am styling like this:
<span class="btn btn-file btn-default">
<span class="text"></span>
<input type="file" id="fileinput" name="uploaded_file" />
</span>
And changing it with this jQuery:
$(document).ready(function(){
$(".text").html("Change Avatar");
$('#fileinput').on('change', function() {
$(".text").text('Avatar Selected');
});
});
By default, it shows 'Change Avatar'. And when somebody selected a photo, it changes to 'Avatar Selected'.
I'd also like it to change from btn-default to btn-selected.
How would I do that?
You could just use the .toggleClass() method to toggle both of the classes.
$(this).closest('.btn').toggleClass('btn-default btn-selected');
$(".text").html("Change Avatar");
$('#fileinput').on('change', function () {
$(".text").text('Avatar Selected');
$(this).closest('.btn').toggleClass('btn-default btn-selected');
});
I'd suggest checking if there is actually a file specified, though, because it's possible that a user could select a file and then unselect it.
$('#fileinput').on('change', function () {
if (this.value) {
// ...
} else {
// ..
}
});
You could also use something like this:
Example Here
$('#fileinput').on('change', function () {
$(".text").text(this.value ? 'Avatar Selected' : 'Change Avatar');
$(this).closest('.btn').removeClass('btn-selected btn-default')
.addClass(this.value ? 'btn-selected' : 'btn-default');
});
In doing so, the parent button element will only have the class .btn-selected if a file is actually selected (even when a file is unselected and then reselected; unlike the first suggestion above).