$locationChangeStart event hitting twice when changing the location - angularjs

I am triggering a confirm modal directive popup when the form is dirty and i am changing the location.
angular.module("top.app.controllers").directive('confirmOnExit', ['$location', 'ConfirmModal', '$timeout',function (location, ConfirmModal, $timeout) {
return {
restrict: 'A',
link: function ($scope, element, attrs) {
$scope.$evalAsync(function () {
var unbindChangeSuccess = $scope.$on('$locationChangeStart', function (event, next, current, e) {
$scope.DirtyForm = ($scope.componentAddForm.$dirty ? $scope.componentAddForm.$dirty : $scope.resourceForm.$dirty)
if ($scope.DirtyForm) {
event.preventDefault();
ConfirmModal.show({
okButtonCaption: "OK",
cancelButtonCaption: "Cancel",
title: "Pending Changes",
text: "Any unsaved data to this record will be lost. Click ‘Continue’ to proceed with this action."
}).then(function () {
event.preventDefault();
$timeout(function () {
window.location = next;
$scope.$apply()
});
})
} else {
};
});
})
}
};
}]);

Related

how to close pop up screen when click out side in screen

I make one pop up screen I open pop up screen on button screen .I want to hide pop up screen when I click outside the screen can is this possible ?
code
http://codepen.io/anon/pen/KpLgpm
angular.module('ionic.example', ['ionic'])
.controller('PopupCtrl', function($scope, $timeout, $q, $ionicPopup) {
$scope.showPopup = function() {
$scope.data = {}
$scope.setDefault = function () {
console.log('Default set', arguments);
$scope.$onClose({ test: 'hello' });
};
$scope.btns = [
{
label: "Hi",
value: "hi"
},
{
label: "Hello",
value: "hello"
}
];
$ionicPopup.show({
template: '',
title: 'Pick a default value',
scope: $scope,
buttons: [
{
text: 'Awesome',
onTap: function(e) { return 'awesome'; }
},
{ text: 'Cool', onTap: function(e) { return 'cool'; } },
{ text: 'Cooler', onTap: function(e) { return 'cooler'; } },
{ text: 'Stuff', onTap: function(e) { return 'stuff'; } }
]
}).then(function(res) {
console.log('Tapped!', res);
}, function(err) {
console.log('Err:', err);
}, function(msg) {
console.log('message:', msg);
});
};
});
angular.element( $window ).on( "click", function( event ) {
if( angular.element( "#popdiv" ).has( event.srcElement || event.target ).length === 0 ) {
// hide popup
}} );
I've found a solution, called "ionic-close-popup".
Install this in your project, add in your index.html, inject in the controller, and register the popup:
https://libraries.io/bower/ionic-close-popup
You can create a directive with below code
angular.module("testapp").directive('clickAnywhereButHere', ["$document", function ($document) {
//click-any-where-but-here
return {
restrict: 'A',
link: function (scope, elem, attr, ctrl) {
var elemClickHandler = function (e) {
e.stopPropagation();
};
var docClickHandler = function () {
scope.$apply(attr.clickAnywhereButHere);
};
elem.on('click', elemClickHandler);
$document.on('click', docClickHandler);
// teardown the event handlers when the scope is destroyed.
scope.$on('$destroy', function () {
elem.off('click', elemClickHandler);
$document.off('click', docClickHandler);
});
}
};
Then in your html element
<div class="mypopup" click-anywhere-but-here="function(){ alert('click out popup event')}"></div>

Can I simplify clicking the enter key with AngularJS?

I already have this code that I came up with:
In my outer controller:
$scope.key = function ($event) {
$scope.$broadcast('key', $event.keyCode)
}
In my inner controller (I have more than one like this)
$scope.$on('key', function (e, key) {
if (key == 13) {
if (ts.test.current) {
var btn = null;
if (ts.test.userTestId) {
btn = document.getElementById('viewQuestions');
} else {
btn = document.getElementById('acquireTest');
}
$timeout(function () {
btn.focus();
btn.click();
window.setTimeout(function () {
btn.blur();
}, 500);
})
}
}
});
Is there another way that I could simplify this using some features of AngularJS that I have not included here?
Please check this gist, https://gist.github.com/EpokK/5884263
You can simply create a directive ng-enter and pass your action as paramater
app.directive('ngEnter', function() {
return function(scope, element, attrs) {
element.bind("keydown keypress", function(event) {
if(event.which === 13) {
scope.$apply(function(){
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
HTML
<input ng-enter="myControllerFunction()" />
You may change the name ng-enter to something different, because ng-** is a reserved by Angular core team.
Also I see that your controller is dealing with DOM, and you should not. Move those logic to other directive or to HTML, and keep your controller lean.
if (ts.test.userTestId) {
btn = document.getElementById('viewQuestions'); //NOT in controller
} else {
btn = document.getElementById('acquireTest'); //NOT in controller
}
$timeout(function () {
btn.focus(); //NOT in controller
btn.click(); //NOT in controller
window.setTimeout(function () { // $timeout in $timeout, questionable
btn.blur(); //NOT in controller
}, 500);
})
What i've done in the past is a directive which just listens for enter key inputs and then executes a function that is provided to it similar to an ng-click. This makes the logic stay in the controller, and will allow for reuse across multiple elements.
//directive
angular.module('yourModule').directive('enterHandler', [function () {
return{
restrict:'A',
link: function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
var key = event.which ? event.which : event.keyCode;
if (key === 13) {
scope.$apply(function () {
scope.$eval(attrs.enterHandler);
});
event.preventDefault();
}
});
}
}
}]);
then your controller becomes
$scope.eventHandler = function(){
if (ts.test.current) {
var btn = ts.test.userTestId
? document.getElementById('viewQuestions')
: document.getElementById('acquireTest');
$timeout(function () {
btn.focus();
btn.click();
window.setTimeout(function () {
btn.blur();
}, 500);
})
}
}
and your markup can then be
<div enter-handler="eventHandler()" ></div>

AngularJS one click on element and click+hold more 5 seconds

I have the div element and I need catch event of one click and click and hold.
If happened one click of this div, i should call function 1 in scope, if click and hold (more of 5 seconds) i should call function 2 in scope.
Create a directive on-click-and-hold and use it.
Directive
directive('onClickAndHold', function ($parse, $timeout) {
return {
link: function (scope, element, attrs) {
var clickAndHoldFn = $parse(attrs.onClickAndHold);
var doNotTriggerClick;
var timeoutHandler;
element.on('mousedown', function () {
$timeout.cancel(timeoutHandler);
timeoutHandler = $timeout(function () {
clickAndHoldFn(scope, {$event: event})
}, 5000)
});
element.on('mouseup', function (event) {
$timeout.cancel(timeoutHandler);
});
if (attrs.onClick) {
var clickFn = $parse(attrs.onClick);
element.on('click', function (event) {
if (doNotTriggerClick) {
doNotTriggerClick = false;
return;
}
clickFn(scope, {$event: event});
scope.$apply();
});
}
}
}
})
Markup
<div on-click-and-hold="f2()" on-click="f1()"></div>
Controller
controller('AppCtrl', function ($scope) {
$scope.f1 = function () {
console.warn('inside f1');
}
$scope.f2 = function () {
console.warn('inside f2');
}
})
If you want to handle only the click event use ng-click instead of on-click-and-hold.
Working Plnkr

Can angular's ngTouch library be used to detect a long click (touch/hold/release in place) event?

My AngularJS app needs to be able to detect both the start and stop of a touch event (without swiping). For example, I need to execute some logic when the touch begins (user presses down their finger and holds), and then execute different logic when the same touch ends (user removes their finger). I am looking at implementing ngTouch for this task, but the documentation for the ngTouch.ngClick directive only mentions firing the event on tap. The ngTouch.$swipe service can detect start and stop of the touch event, but only if the user actually swiped (moved their finger horizontally or vertically) while touching. Anyone have any ideas? Will I need to just write my own directive?
Update 11/25/14:
The monospaced angular-hammer library is outdated right now, so the Hammer.js team recommend to use the ryan mullins version, which is built over hammer v2.0+.
I dug into ngTouch and from what I can tell it has no support for anything other than tap and swipe (as of the time of this writing, version 1.2.0). I opted to go with a more mature multi-touch library (hammer.js) and a well tested and maintained angular module (angular-hammer) which exposes all of hammer.js's multi-touch features as attribute directives.
https://github.com/monospaced/angular-hammer
It is a good implementation:
// pressableElement: pressable-element
.directive('pressableElement', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $elm, $attrs) {
$elm.bind('mousedown', function (evt) {
$scope.longPress = true;
$scope.click = true;
// onLongPress: on-long-press
$timeout(function () {
$scope.click = false;
if ($scope.longPress && $attrs.onLongPress) {
$scope.$apply(function () {
$scope.$eval($attrs.onLongPress, { $event: evt });
});
}
}, $attrs.timeOut || 600); // timeOut: time-out
// onTouch: on-touch
if ($attrs.onTouch) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouch, { $event: evt });
});
}
});
$elm.bind('mouseup', function (evt) {
$scope.longPress = false;
// onTouchEnd: on-touch-end
if ($attrs.onTouchEnd) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouchEnd, { $event: evt });
});
}
// onClick: on-click
if ($scope.click && $attrs.onClick) {
$scope.$apply(function () {
$scope.$eval($attrs.onClick, { $event: evt });
});
}
});
}
};
})
Usage example:
<div pressable-element
ng-repeat="item in list"
on-long-press="itemOnLongPress(item.id)"
on-touch="itemOnTouch(item.id)"
on-touch-end="itemOnTouchEnd(item.id)"
on-click="itemOnClick(item.id)"
time-out="600"
>{{item}}</div>
var app = angular.module('pressableTest', [])
.controller('MyCtrl', function($scope) {
$scope.result = '-';
$scope.list = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
{ id: 7 }
];
$scope.itemOnLongPress = function (id) { $scope.result = 'itemOnLongPress: ' + id; };
$scope.itemOnTouch = function (id) { $scope.result = 'itemOnTouch: ' + id; };
$scope.itemOnTouchEnd = function (id) { $scope.result = 'itemOnTouchEnd: ' + id; };
$scope.itemOnClick = function (id) { $scope.result = 'itemOnClick: ' + id; };
})
.directive('pressableElement', function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $elm, $attrs) {
$elm.bind('mousedown', function (evt) {
$scope.longPress = true;
$scope.click = true;
$scope._pressed = null;
// onLongPress: on-long-press
$scope._pressed = $timeout(function () {
$scope.click = false;
if ($scope.longPress && $attrs.onLongPress) {
$scope.$apply(function () {
$scope.$eval($attrs.onLongPress, { $event: evt });
});
}
}, $attrs.timeOut || 600); // timeOut: time-out
// onTouch: on-touch
if ($attrs.onTouch) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouch, { $event: evt });
});
}
});
$elm.bind('mouseup', function (evt) {
$scope.longPress = false;
$timeout.cancel($scope._pressed);
// onTouchEnd: on-touch-end
if ($attrs.onTouchEnd) {
$scope.$apply(function () {
$scope.$eval($attrs.onTouchEnd, { $event: evt });
});
}
// onClick: on-click
if ($scope.click && $attrs.onClick) {
$scope.$apply(function () {
$scope.$eval($attrs.onClick, { $event: evt });
});
}
});
}
};
})
li {
cursor: pointer;
margin: 0 0 5px 0;
background: #FFAAAA;
}
<div ng-app="pressableTest">
<div ng-controller="MyCtrl">
<ul>
<li ng-repeat="item in list"
pressable-element
on-long-press="itemOnLongPress(item.id)"
on-touch="itemOnTouch(item.id)"
on-touch-end="itemOnTouchEnd(item.id)"
on-click="itemOnClick(item.id)"
time-out="600"
>{{item.id}}</li>
</ul>
<h3>{{result}}</h3>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
Based on: https://gist.github.com/BobNisco/9885852
The monospaced angular-hammer library is outdated right now, so the Hammer.js team recommend to use the ryan mullins version, which is built over hammer v2.0+.

Unable to watch/observe scope?

<button-large color="green" click="createWorkstation()" busy="disableSave()" busyLabel="Saving...">Save</button-large>
I'm not able to watch changes to the output of disableSave(). The console.log()'s shown in my directive are never triggered when the output of .busy is changed. What am I doing wrong?
directive('buttonLarge', function () {
return {
scope: {
busy: '&',
click: '&'
},
replace: true,
restrict: 'E',
transclude: true,
template: '<button class="buttonL" ng-transclude/>',
link: function (scope, element, attrs) {
//when the button is busy, disable the button
if (angular.isDefined(scope.busy())) {
scope.$watch(scope.busy(), function () {
console.log('watched');
});
attrs.$observe(scope.busy(), function () {
console.log('observed');
});
}
//setup click event - https://groups.google.com/forum/#!topic/angular/-uVE5WJWwLA
if (angular.isDefined(scope.click)) {
element.bind('click', scope.click);
}
}
}
})
Controller
$scope.newWorkstationDialog = function (workflowProcess) {
var d = $dialog.
dialog({
resolve: {
workflowProcess: function () {
return workflowProcess;
}
}
}).
open('/partials/admin/'+workflowProcess.entity.slug+'/setup.htm', ['$scope', 'dialog', ..., function ($scope, dialog, ...) {
$scope.saving = false;
/* Create the workstation */
$scope.createWorkstation = function () {
console.log('saving');
$scope.saving = true;
$timeout(function () {
$scope.saving = false;
console.log('stopped saving');
}, 1000);
}
//Should the save button be disabled?
$scope.disableSave = function () {
return $scope.saving;//|| $scope.form.$valid;
}
$scope.cancel = function () {
dialog.close();
}
}]);
}
Your syntax of watching in not correct .You should be not using scope when doing watch because internally it use $parse service which internally attach scope . So you need to modify your code as below
1st option
scope.$watch(function(){
return scope.busy()
}, function (newvalue,oldvalue) {
console.log('watched');
});
2nd option
scope.$watch('busy()', function (newvalue,oldvalue) {
console.log('watched');
});

Resources