How can I open an Angular Material menu from a controller function? - angularjs

I'm doing a check for a user state and would like to enable and disable a menu accordingly.
In the markup:
<a ... ng-click="ctrl.userMenu($event)"></a>
And in the controller:
ctrl.userMenu = function (e) {
if (ctrl.user.has.something) {
e.preventDefault();
return false;
} else {
ctrl.openMenu($mdOpenMenu, e);
}
};
However, this doesn't trigger the menu if the else case is true. I suspect a scope issue. I've also tried wrapping the menu service call in an anonymous function. The menu opens as expected if the call is made directly from the ng-click directive. Thanks for any assistance.

Turns out I was forgetting to pass the menu service along with the ng-click directive:
<a ... ng-click="ctrl.userMenu($mdOpenMenu, $event)"></a>
// ---------------------------------^
ctrl.userMenu = function (m, e) {
// -----------------------^
if (ctrl.user.has.something) {
e.preventDefault();
return false;
} else {
ctrl.openMenu(m, e);
// -----------^
}
};

Related

Angular JS upload file occurs twice in IE

I have a directive that handles uploading file and shows it in a list. For this I have a custom button for opening up the explorer. And after user selects a file from the explorer system shows the file name twice in the list. After debugging I realized it's calling the "onClick" method twice, once when the user clicks it (duh) and some mysterious event invokes it again. I think it's the scope.$apply part but can't be sure. Here's my code snippet:
<div data-ng-click="addFile($event)">
<span class="icon-small icon-add"></span>
</div>
Angular JS:
scope.addFile = function (event) {
if (event.originalEvent == null || !(event.originalEvent instanceof MouseEvent)) {
return;
}
if (!hiddenInputElementNode) {
//inject the hidden HtmlInputFile element and bind to the click event
hiddenInputElementNode = angular.element(
"<input accept='application/pdf,audio/*' type='file' class='hidden' multiple />");
hiddenInputElementNode.insertAfter(event.target);
}
//bind to the inputElementNode change event
hiddenInputElementNode.bind('change', function () {
angular.forEach(hiddenInputElementNode[0].files, function (dataFile) {
scope.$apply(
scope.selectedFiles.push({
name: dataFile.name,
data: dataFile
}));
});
this.value = null;
hiddenInputElementNode.unbind('change');
});
$timeout(function () {
if (!!hiddenInputElementNode) {
hiddenInputElementNode.click();
}
}, 0, false);
};
Even weirder this.value = null doesn't nullify the value!
Try by changing your javascript code for this one:
scope.addFile = function (event) {
if (event.target.tagName.toUpperCase() === "DIV") {
if (!hiddenInputElementNode) {
//inject the hidden HtmlInputFile element and bind to the click event
hiddenInputElementNode = angular.element(
"<input accept='application/pdf,audio/*' type='file' class='hidden' multiple />");
hiddenInputElementNode.insertAfter(event.target);
}
//bind to the inputElementNode change event
hiddenInputElementNode.bind('change', function () {
angular.forEach(hiddenInputElementNode[0].files, function (dataFile) {
scope.$apply(
scope.selectedFiles.push({
name: dataFile.name,
data: dataFile
}));
});
this.value = null;
hiddenInputElementNode.unbind('change');
});
$timeout(function () {
if (!!hiddenInputElementNode) {
hiddenInputElementNode.click();
}
}, 0, false);
}
};
I believe that you ng-click event is been fired twice because of the span inside the div (i had a similar problem with IE too).

Widget toggle functionality with $compile

I need to implement toggle functionality for the widget. When the user clicks on the minimization button then widget should shrink and expand when click on maximize button respectively.
I'm trying to achieve this functionality with below piece of code.
Functionality working as expected but it is registering the event multiple times(I'm emitting the event and catching in the filterTemplate directive).
How can we stop registering the event multiple times ?
Or
Is there anyway to like compiling once and on toggle button bind the template/directive to DOM and to make it work rest of the functionality .
So could you please help me to fix this.
function bindFilterTemplate(minimize) {
if ($scope.item && !minimize) {
if ($scope.item.filterTemplate) { // filter template is custom
// directive like this
// "<widget></widget>"
$timeout(function () {
var filterElement = angular.element($scope.item.filterTemplate);
var filterBody = element.find('.cls-filter-body');
filterElement.appendTo(filterBody);
$compile(filterElement)($scope); // Compiling with
// current scope on every time when user click on
// the minimization button.
});
}
} else {
$timeout(function () {
element.find('.cls-filter-body').empty();
});
}
}
bindFilterTemplate();
// Directive
app.directive('widget', function () {
return {
restrict: 'E',
controller: 'widgetController',
link: function ($scope, elem) {
// Some code
}
};
});
// Controller
app.controller('widgetController', function ($scope) {
// This event emitting from parent directive
// On every compile, the event is registering with scope.
// So it is triggering multiple times.
$scope.$on('evt.filer', function ($evt) {
// Server call
});
});
I fixed this issue by creating new scope with $scope.$new().
When user minimizes the widget destroying the scope.
Please let me know if you have any other solution to fix this.
function bindFilterTemplate(minimize) {
// Creating the new scope.
$scope.newChildScope = $scope.$new();
if ($scope.item && !minimize) {
if ($scope.item.filterTemplate) {
$timeout(function () {
var filterElement = angular.element($scope.item.filterTemplate);
var filterBody = element.find('.cls-filter-body');
filterElement.appendTo(filterBody);
$compile(filterElement)($scope.newChildScope);
});
}
} else {
$timeout(function () {
if ($scope.newChildScope) {
// Destroying the new scope
$scope.newChildScope.$destroy();
}
element.find('.cls-filter-body').empty();
});
}
}

How to hide the menu icon when back button is shown?

I have a side menu with:
<ion-side-menus enable-menu-with-back-views="true">
So it is accesable from each view of my app. So now when there is a back view i have a back icon AND the menu icon in the top left nav-bar. How can i disable the menu-icon when there is a back-icon?
The method from here:
$scope.$on('$ionicView.beforeEnter', function (e, data) {
if (data.enableBack) {
$scope.$root.showMenuIcon = false;
} else {
$scope.$root.showMenuIcon = true;
}
});
Is not working! Because it is never called! Maybe $ionicView.beforeEnter does not exist anymore? At least it is never fired.
I solved that problem with this code in each view controller
$scope.$on('$ionicView.beforeEnter', function () {
$ionicSideMenuDelegate.canDragContent(false);
});
$scope.$on('$ionicView.leave', function () {
$ionicSideMenuDelegate.canDragContent(true);
});
Hope it helps
don't forget to add $ionicSideMenuDelegate to your controller

Animation End event Zurb Foundation for apps

I have a scroll up event and a scroll down event. I am trying to use FoundationAPI.animate to slideInUp a div on scroll up and then slideOutBottom on scroll down. The original state of the div has a class of hide because without scrolling - it lives off the bottom of the page. I have this working except for one problem. When the slideOutBottom animation is finished, the hide class no longer is in the div, so it shows after the slideOutBottom completes. I want it to stay hidden like it was at state 0.
Code:
$scope.scrollUp = function() {
if ($scope.lock == false ){
console.log('scrolling up');
$scope.lock = true;
FoundationApi.animate($('.footer-bar'), $state, "slideInUp", "hide");
console.log($state);
}
};
$scope.scrollDown = function() {
if ($scope.lock == true){
console.log('scrolling down');
$scope.lock = false;
FoundationApi.animate($('.footer-bar'), !$state, "hide", "slideOutBottom");
// setTimeout(function() {
// $('.footer-bar').addClass('hide');
// }, 999);
}
};
How do I access a callback for the FoundationAPI.animate(4) function such that it fires when it is complete? The commented out timeout works, but after the slideOutBottom finishes, the footer-bar appears, then the hide class gets applied. This causes the div to blink quickly. Anyone? The documentation on FoundationApi is lacking right now...
Not an answer but a solution. This a more natural AngularJS approach:
Controller:
function myController($scope, ...) {
$scope.lock = false;
$scope.scrollUp = function() {
if ($scope.lock == false ){
$scope.lock = true;
$scope.$apply();
}
};
$scope.scrollDown = function() {
if ($scope.lock == true){
$scope.lock = false;
$scope.$apply();
}
};
}
View:
<a id="footer" class="slideInUp slideOutBottom footer-bar" ng-hide="lock" zf-open="myModal">
<div>Click Me</div>
</a>
So the scroll events I bind with a directive on the particular view container; this determines the logic for when we detect the scroll up/down events. From the directive I call these functions that are defined the scoped controller (above). These are bound 2-ways with the "lock" variable accessible in this scope.
Note that the way the animations are triggered are with the classes in the anchor. I think there are defined enter animations and defined exit animations which are both present in the class. So ng-hide kicks in depending on the boolean value of the $scope.lock variable in my controller.
Totally not clear - I guess would be more clear to an Angular expert

looking for "angular way" of closing open menu when page is clicked

This is what I have, and it works (thankfully, angular supports jQuery out of the box if its loaded)....but I'm wondering what the "angular way" is to accomplish this.
I want to close the open menu if you click anywhere else on the page, but the menu:
<body ng-click="onClickPage($event)">
//app controller:
$scope.onClickPage = function(e){
$log.log(e);
$rootScope.$broadcast('click:page', e);
};
//navbar controller
$rootScope.$on('click:page', function(ev, e){
var $el = $(e.target);
if ( !$el.parents('.menu').length && !$el.hasClass('.menu') ) {
$log.log('hide dropdown');
$scope.hideDropdown();
}
});
That might depend on how your dropdown is implemented but a general idea is to bind/unbind click event handler to the $document when open/close the dropdown.
By doing this way,it doesn't polute global event listeners and $rootScope while the dropdown is not opened.
function onDocumentClicked(e) {
var dropdownEl = angular.element('.dropdown');
if (e && dropdownEl && dropdownEl[0].contains(e.target)) {
return; // do nothing if clicked inside the dropdown
}
closeDropdown();
$scope.$apply();
}
function openDropdown() {
if (!$scope.dropdown.isOpen) {
$scope.dropdown.isOpen = true;
$document.bind('click', onDocumentClicked);
}
}
function closeDropdown() {
if ($scope.dropdown.isOpen) {
$document.unbind('click', onDocumentClicked);
$scope.dropdown.isOpen = false;
}
}
For the full example see: http://plnkr.co/edit/mbx0sLnPetctlWNYdpJC?p=preview

Resources