I would like to disable the drag down to close gesture of mdbottomsheet. I've found a work around on scripts but I'm not sure where to put the code. Thanks for the help.
As you say that angular-material doesn't provide any option to disable it, obviously you will have to make changes in its source code.
Now, you haven't mentioned whether you want to disable it at specific places or turn drag-down-to-close for bottomSheets everywhere.
1) In case of latter, it would be quite straightforward, as the only thing you need to do is remove the event listeners for drag events.
If you use angular-material.js file, heres what you can do:
Find the function BottomSheet(element, parent). This function basically registers the drag events which close the sheet. We need make it not attach the listeners.
Reduce it to:
function BottomSheet(element, parent){
return {
element: element,
cleanup: angular.noop
};
}
The cleanup function basically de-registers the listeners on drag event.This function is called when the scope of the bottomSheet is destroyed. To make minimal changes, we have just reduced the cleanup function to do nothing.
2) If you want to be able to pass an option while creating the sheet in your controller, you do the same thing, but conditionally based on the option you pass. Wont write the code because I assume you know how angular works, but here are the steps:
=> Add a boolean variable along with other options(template,scope,etc. ). Lets call it dragDownToClose.
=> In the defaults injector function inside the provider function of MdbottomSheet , assign it a default value (true/false).
=>Pass this along with element and parent during instantiation of BottomSheet() inside the onShow function.
=> So BottomSheet() will now have three argument - dragDownToClose being the new one.
=> As we did in the former case, return the element without any handler attached when the value is false, and let the original function be when its true.
Of-course there are various ways in which you can actually implement this. However, I hope you get the idea.
First, inject $element into your controller. You known what AngularJS $element do, right?
Then we both known that the drag events are registered in BottomSheet
parent.on('$md.dragstart', onDragStart)
.on('$md.drag', onDrag)
.on('$md.dragend', onDragEnd);
So, the simple solution is: Remove those events, override those events... without override the function BottomSheet, right?
$element
.on('$md.dragstart', function (ev) {
return false;
})
.on('$md.drag', function (ev) {
return false;
})
.on('$md.dragend', function (ev) {
return false;
});
Something still wrong here! The backdrop still draggable! So, we do the same for backdrop
var parent = $element.parent();
var backdrop = parent.find('md-backdrop');
backdrop
.on(blah blah blah
These is code in case you are asking for
You can try
$mdBottomSheet.show({
template: *yourTemplate*,
clickOutsideToClose:false
})
this will not let the user close the bottom sheet even with drag or click outside.
Related
I am working on a problem wherein I am required to pick-up keydown events (specifically ctrl+p and then point to a print function which already exists) on a certain custom directive and under certain conditions (a certain tab should be selected). My current approach is to bind the keydown event on the document itself, broadcast it and then listen to it in the required custom directive. Following is the code I have placed in the app.run.. block -
angular.element($document).on('keydown', function(evt) {
if(evt.ctrlKey && evt.key==='p'){
$rootScope.$broadcast('printOnKeyPress');
}
});
This part is working as expected, the problem arises when I try to handle it in the required controller of the custom directive as follows:
$scope.$on('printOnKeyPress', function() {
//point to existing print function
}
This is where the problem arises. It goes into the print function but still the output is incorrect. I am missing something and I can't figure out what.
Also, this is not a good approach but I have searched and am unable to find a possible solution to just bind the keydown event on that custom directive itself (the component only appears if a document is selected).
(ng-keydown won't also work here)
Any help is appreciated!
You could put it in the directive and use the scope destroy event to remove the listener.
Within directive link function:
function keyHandler(evt) {
if (evt.ctrlKey && evt.key === 'p') {
// do your print
}
}
angular.element($document).on('keydown', keyHandler);
scope.$on('$destroy', function() {
angular.element($document).off('keydown', keyHandler);
});
Button works perfectly on touchscreen when clicking or left-swiping:
<button ng-click="click(it)" ng-swipe-left="leftSwipe(it)" />
However, on the desktop where left-swipe is accomplished by combining a mouse-click with a left-drag, the click event is also fired.
Is there any way I could suppress this?
Well I didn't find anything simple, so here is one workaround and one semi-suppression, both rely on $timeout, but given you rely on human interaction I think we're ok.
Semi-Suppression: we'll want to ignore clicks this digest cycle and return to listen to the event next digest cycle.
$scope.leftSwipe = function(event){
angular.element(event.target).unbind('click');
$timeout(function(){angular.element(event.target)
.bind('click', function(it) {$scope.click(it);})},0);
};
Here we pass the event to the 'left-swipe' function in order to get the target element, if you do not want to pass the event as parameter (depending on your code) you can grab an element hardcoded id either with query ($('#yourButtonId')) or without (document.querySelector('#yourButtonId')) and use it instead. Take note that re-handling click event here will require passing the params (in your case 'it'?) again, that is why it is wrapped in another function and not called directly.
Workaround: I'd consider this much simpler but it's up to you and the code.
var hasSwiped = false;
$scope.click = function(event){
if (!hasSwiped){
...
}
};
$scope.leftSwipe = function(event){
hasSwiped = true;
$timeout(function(){ hasSwiped = false; },1000);
};
Here we simply create a variable 'hasSwiped' and set it to true once swiping and reseting it to false only after the 'click' event has fired (it depends on the app, but one second sounds reasonable to me between a swipe and click). In the click event just check this flag if raised or not.
Hope this helps,
Good Luck!
I have a tab-like view on my page and I have a variety of events which need to set the focus to one of the tabs. (showing/hiding the div is easy since I just use a model variable.) The events that cause a div to be focused are clicking on the tab header, data load completion, and initial loading. I know where to intercept all these events, but I'm not sure how I tell the other element to set the focus (none of the intercept sites know about the other element, only about the model).
I've looked around but can't find a good reference for this. I assume I want to listen for some message and post it from the various other locations.
How does one setup this type of messaging event?
ANSWER: I built on the answer and came up with the below directive. It combines both a focus and show state for the div.
newsendApp.directive('showAndFocus', function() {
return {
link: function(scope, element, attr){
scope.$on('SetArticlesListFocus',function() {
if( scope.$eval( attr.showAndFocus ) ) {
$(element).focus();
}
})
scope.$watch(attr.showAndFocus, function(value) {
if( value ) {
$(element).show();
setTimeout( function() { $(element).focus(); }, 0 );
} else {
$(element).hide();
}
})
}
}
});
If an event occurs which may require resetting the focus then I do: $rootScope.$broadcast( 'SetArticlesListFocus' );
You can broadcast an event, and also have listeners that perform an action once such a broadcast has occured.
You can use $scope.$broadcast('changed-tab', objectSentWithBroadcast). The second parameter is an object you can optionally send, like the tab you want to focus on.
Then you would have a listener like this where you can select the tab wanted:
$scope.$on('changed-tab', function(e, objectSentWithBroadcast) {
// do something here to select the tab
});
Have a look at this issue raised in angularjs.
https://github.com/angular/angular.js/issues/1277#issuecomment-16012024
Here is the final plunk that creates a ng-focus directive that manages two way data binding between a variable and an element's focus state.
http://plnkr.co/edit/bntEsfngnJKuneg2raD1
This will allow you to bind a model to an elements focus state and then setting or unsetting this variable will make the element gain / lose focus. You will have to note that if you are using div's etc ( which are non-focusable by default! ) you will need to set tabIndex = -1 on them. Apart from that it should work fine and suit your need for manipulating models pretty well.
I have a page with 50 hidden checkboxes, and I want to be able to toggle each checkbox by clicking on a visible link. The actual checkboxes have to stay hidden...so... Is there a better way to do this, with a JS function so I don't have to include the entire onclick in each link? And I use mootools, not jQuery.
This works to activate a checkbox:
Select
But to toggle it, this works:
onclick="if (event.target.tagName != 'INPUT') document.getElementById('field_select_temp_professional_10').checked = !document.getElementById('field_select_temp_professional_10').checked"
None of what you posted is actually mootools code, you may as well not use mootools...
Markup:
Select
js in your domready:
document.getElements("a.add_app").addEvents({
click: function(e) {
if (e.target.get("tag") != 'input') {
var checkbox = document.id("field_select_p" + this.get("data-id"));
checkbox.set("checked", !checkbox.get("checked"));
}
}
});
If you have 100+ then I suggest you look at using event delegation from mootools-more and add just one event to the parent instead of creating 100 events and storing 100 functions that deal with it.
This is coding to patterns, and it involves changing your markup to make things work. You can also make the change based upon walking the DOM in relation to the clicked item, e.g. this.getParent().getElement("input[type=checkbox]"), or something can mean you don't need to store a relative id in the element itself.
So today I just came across the 'live()' function that binds any future and past elements to the whatever event you choose, such as 'onclick'.
Right now I'm having to set up buttons like the following each time I load a new button via ajax ...
$('a.btn.plus').button({icons:{primary:'ui-icon-plusthick'}});
$('a.btn.pencil').button({icons:{primary:'ui-icon ui-icon-pencil'}});
$('a.btn.bigx').button({icons:{primary:'ui-icon ui-icon-closethick'}});
So, instead of calling these lines each time I use ajax to add a new button, is there a similar way to tell JQuery to setup my buttons ANYTIME I add new ones?
Thanks for any help!
Mmh not really. But there is the function .ajaxSuccess() which is triggered whenever an Ajax call is successful. So you could do:
$('body').ajaxSuccess(function() {
$('a.btn.plus').button({icons:{primary:'ui-icon-plusthick'}});
$('a.btn.pencil').button({icons:{primary:'ui-icon ui-icon-pencil'}});
$('a.btn.bigx').button({icons:{primary:'ui-icon ui-icon-closethick'}});
});
But this will run on any links with the classes, not only on the new ones. But if you append them on a time (i.e. not multiple a.btn.plus at once) you might be able to use the :last selector (a.btn.plus:last).
You can also create a function and just that from your callback functions:
function links() {
$('a.btn.plus').button({icons:{primary:'ui-icon-plusthick'}});
$('a.btn.pencil').button({icons:{primary:'ui-icon ui-icon-pencil'}});
$('a.btn.bigx').button({icons:{primary:'ui-icon ui-icon-closethick'}});
}
and in the Ajax call:
$.ajax({
//...
success: function(msg){
links();
}
});
This way you can pass the parent element to the function in order to find the link only inside this element (so the code would only work on the new links).
A last option would be generate a custom event but in the end this would be similar to just doing a function call in your case so you gain not much.
You can use delegate in your success function too
$("body").delegate("a.btn", "hover", function(){
$(this).toggleClass("hover");
});
There is a Jquery Plugin called livequery which covers your requirements.
I like to think of this plugin as Jquery .live() but without the need for an event ('click') etc.
You can find more info here//
Jquery - Live Query Plugin