ng-click on firing on mobile or tablet devices - angularjs

On page load i have a controller that calls a service and then binds the returned data to some $scope.objects:
app.controller("MainController", function($scope, $http, serviceGetData) {
serviceGetData.getData(function(data) {
$scope.LoginCount = data.LoginCount;
$scope.ProductInfo = data.ProductInfo;
$scope.ProfileInfo = data.ProfileInfo;
// Delayed binding
$scope.OrderHistory = { History: [] };
}
$scope.populateModel = function(model, values) {
var isArray = $.isArray(values);
$.each(values, function(key, value) {
if (isArray) {
key = this.key;
value = this.value;
}
if (model[key] !== value) {
model[key] = value;
}
});
};
}
And in my HTML, i try to bind $scope.OrderHistory by:
<h1><a href="#" ng-click="populateModel(OrderHistory , { History: OrderEntries })" >View order details</a></h1>
This is fine when viewing on laptops/desktops, but not working in tablet and mobile devices e.g. iphone/ipad

Try to add the ngTouch. From documentation:
A more powerful replacement for the default ngClick designed to be used on touchscreen devices. Most mobile browsers wait about 300ms after a tap-and-release before sending the click event. This version handles them immediately, and then prevents the following click event from propagating.
Requires the ngTouch module to be installed.

I had the same problem.
I tried adding the ngTouch library and dependency, but was still having a problem.
My static <div> elements which contained an ng-click worked fine, but the div's which were created dynamically (in an ng-repeat on the same webpage) which contained an ng-click didn't work. Tapping on them just didn't do anything.
This happened on my iPhone 6, my iPad and in Google Chrome when I asked to view my webpage on any of the device types. When I viewed the same webpage in IE or regular Chrome, all of the ng-clicks worked fine.
The solution was to use the ngMobileClick directive described here and changing my ng-click's to ng-mobile-click.
After doing that, my dynamically created click events did get triggered normally when I tapped on them on a device.
Very odd.

Had a similar issue where the ng-click inside of a ng-repeat would not trigger.
I fixed this by adding $event.stopPropagation()
Ex:
<li ng-repeat="option in $scope.options">
<span><a ng-click="trigger(); $event.stopPropagation()"></a></span>
</li>

Related

Can't get CSS in AngularJS app to update instantly

I’m hoping there are some Angular 1.x experts who can show me what I’m doing wrong. I have a simple function to update which of 3 buttons in a “tab group” is the current one. This function is called whenever any of the buttons is clicked.
$scope.updateFilter = function (type, value) {
// Additional unrelated code here ...
document.getElementsByClassName('active')[0].className = document.getElementsByClassName('active')[0].className.replace(' active', '');
document.getElementById('tabButton_' + value).className += ' active';
$scope.$apply();
};
The background color of the current button is indeed highlighted but only AFTER one clicks elsewhere on the screen. In other words, it’s not updated instantly like it should.
Any ideas how to correct this?
It's hard to diagnose the issue without seeing some more code or a reproduction of your existing issue. However, from the above, you are certainly not doing the "angularjs" way. A more angular approach would be to use bindings and update the model as the user clicks different button options. A very basic (and ugly styled) example:
angular.module('myApp', [])
.controller('MainController', function () {
var self = this;
self.$onInit = function $onInit() {
// These will be ng-repeated over for the example
self.buttons = [
'Option 1',
'Option 2',
'Option 3'
];
// This is the model binding that will drive the active style
self.activeIndex = 0;
};
self.setActiveIndex = function setActiveIndex(index) {
// This is called on button click and updates the model used
// for the active button styling
self.activeIndex = index;
};
});
.active {
background: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<!-- repeat the buttons. when clicked, call controller method to update model's active index -->
<div ng-app="myApp" ng-controller="MainController as $ctrl">
<button ng-repeat="b in $ctrl.buttons" type="button" ng-class="{active: $ctrl.activeIndex===$index}" ng-click="$ctrl.setActiveIndex($index)">{{::b}}</button>
</div>
Take aways:
You probably shouldn't be doing DOM manipulation. Use existing directives and model binding, else you are losing many of the benefits you are supposed to get from angularjs.
Don't call $scope.$apply(). Angular will do this for you if you are using an ng-click in your template (which you probably should instead of building the event listeners yourself).

Angular ng-Touch swipe event fails on Windows 8.1 and Windows Phone Overflow

I'm currently using Angular 1.3.13, angular-touch 1.3.15, and Cordova 4.1.2 with the following code.
<div ng-controller="myController">
<ul>
<li ng-swipe-right="swipeRight()">Swipe Me</li>
<li ng-swipe-right="swipeRight()">Swipe Me</li>
<li ng-swipe-right="swipeRight()">Swipe Me</li>
<li ng-swipe-right="swipeRight()">Swipe Me</li>
</ul>
</div>
With the following controller.
angular.module('myApp').controller('myController', function($scope, messages) {
$scope.swipeRight = function() {
console.log('swipe-right');
}
});
In the Windows Phone and Windows 8.1 Emulator the swipe event fires fine until the list gets large enough to trigger an overflow the swipe events stop firing. If you set the overflow property to hidden then the swipe events resume working. Has anyone seen this issue before? Please let me know if more info is needed.
I have similar problem with Windows store app. Everything works fine for mouse mode control, but for touch mode swipe events do not work.
http://hammerjs.github.io/ - Registration of HammerJS listener magically helps, angular swipe events start working.
<div id="myElement" ng-cloak ng-swipe-left='next()' ng-swipe-right='previous()'>
...
//Register swipe event
var mc = new Hammer($('#myElement')[0]);
// listen to events...
mc.on("swipeleft swiperight", function (ev) {
if(ev.type == "swipeleft")
{
//$scope.previous(); //No need for this
}
else if (ev.type == "swiperight") {
//$scope.next();
}
});
//Angular Swipe functions
$scope.next = function() {
//Do something
};
$scope.previous = function() {
//Do something
};
I hope this may help.

ng-show directive takes too long to update the dom after trigger

The app has a controller, that uses a service to create an instance of video player. The video player triggers events to show progress every few seconds. When the video reaches to a certain point, I want to show a widget on top of the video player.
The view has the widget wrapped in ng-show directive.
It takes more then 60 seconds for the dom element to receive the signal to remove the ng-hide class after the event has been triggered and the values have been populated.
If I try to implement this using the plain dom menthod (like document.getElementById(eleId).innerHTML = newHTML), the update is instant.
What am I doing wrong? Here is the complete sequence in code:
Controller:
MyApp.controller('SectionController', ['$scope', 'PlayerService'], function($scope, PlayerService){
$scope.createPlayer = function() {
PlayerService.createPlayer($scope, wrapperId);
}});
Service:
MyApp.service('PlayerService', [], function(){
this.createPlayer=function(controllerScope, playerWrapper){
PLAYER_SCRIPT.create(playerWrapper) {
wrapper : playerWrapper,
otherParam : value,
onCreate : function(player) {
player.subscribe(PLAY_TIME_CHANGE, function(duration){
showWidget(controllerScope, duration);
})
}
}
}
function showWidget(controllerScope, duration) {
if(duration>CERTAIN_TIME) {
$rootScope.widgetData = {some:data}
$rootScope.showWidget = true;
}
}});
View:
<div ng-show="showWidget"> <div class="wdgt">{{widgetData.stuff}}</div> </div>
Solved it! $scope.$apply() did the trick.
My guess is, due to other complex logic ad bindings inside the app, there was a delay in computing the change by angular the default way.
#floribon Thanks for the subtle hint about "complex angular stuff".
The code inside the service function changed to:
function showWidget(controllerScope, duration) {
if(duration>CERTAIN_TIME) {
$rootScope.widgetData = {some:data}
$rootScope.showWidget = true;
$rootScope.$apply();
}}
Do you have complex angular stuff within your hidden view?
You should try to use ng-if instead of ng-show, the difference being that when the condition is false, ng-if will remove the element from the DOM instead of just hidding it (which is also what you do in vanilla JS).
When the view is simply hidden using ng-show however, all the watchers and bindings within it keep being computed by Angular. Let us know if ng-if solve your problem, otherwise I'll edit my answer.

How to update onsen-ui states?

I have a simple onsen-ui sample page.
It's built in Monaca (http://monaca.mobi) with Onsen UI 1.0.4.
The page contains two onsen-ui buttons (ons-button), their visibility is bound a javascript method via angular js. The buttons are mutually exclusive, meaning when button 1 is visible, button 2 must be hidden - and the other way around.
When either button is clicked, an internal flag is changed and the other button is shown.
Problem is: the visibility of the buttons is not applied correctly when the page first loads.
It only works when the user manually clicks one of the buttons.
As a counter example, there are also two normal HTML buttons on the page - these buttons work correctly as soon as the page is loaded.
Can you give me any advice? Do I have to manually force a refresh when the page is loaded?
Thank you very much in advance!
HTML code:
<div ng-controller="AppCtrl">
<strong>Click To Toggle</strong> <br>
<button ng-click="startTracking()" ng-hide="isTrackingRunning()"><strong>On</strong></button>
<button ng-click="stopTracking()" ng-show="isTrackingRunning()"><strong>Off</strong></button>
<ons-button ng-click="startTracking()" ng-hide="isTrackingRunning()">Start Tracking</ons-button>
<ons-button ng-click="stopTracking()" ng-show="isTrackingRunning()">Stop Tracking</ons-button>
</div>
JS code:
angular.module('SotraMon', ['onsen.directives'])
.controller('AppCtrl',['$scope', function($scope){
var trackingRunning = false;
$scope.isTrackingRunning = function() {
console.log("getter called, returning " + trackingRunning);
return trackingRunning;
}
$scope.startTracking = function() {
trackingRunning = true;
}
$scope.stopTracking = function() {
trackingRunning = false;
}
}]);
I can reproduce in Onsen UI 1.0.4. One solution is to use external span tag s.t.
<span ng-hide="isTrackingRunning()"><ons-button ng-click="startTracking()">Start Tracking</ons-button></span>
<span ng-show="isTrackingRunning()"><ons-button ng-click="stopTracking()">Stop Tracking</ons-button></span>

Sencha: How to trigger click event on li element

I've been unable to figure out how to manually fire DOM events.
Here, for example, is my attempt to fire the "click" event for a li
Ext.DomQuery.select('#mapRoutesPanel ol li:nth-child('+(index+1)+')')[0].click();
It's working fine on google chrome, but when i build android native app of same application it gives me error
Uncaught TypeError: Object #<HTMLLIElement> has no method 'click'
Ext JS provides its methods for search elements in DOM tree.
Look Sencha Fiddle - its sencha touch app, i tested it on my android(opera) and iphone(safari) its work for me
Something like this:
var liElement = Ext.get('mapRoutesPanel').down('ol li:nth-child(' + (index + 1) + ')');
Have you tried
Ext.dom.Element-method-fireEvent?
Ext.DomQuery.select('#mapRoutesPanel ol li:nth-child('+(index+1)+')')[0].fireEvent('click')
Not all touch device browsers/apps support the click event because it is a mouse event. Why don't you try using Sencha's normalized event system to bind a click handler to the component, you can then check if the <li/> was clicked inside the component's click event handler.
Sencha has already done the work for us so we can handle clicks & taps in the same manner, so take advantage of it.
Btw, event delegation from a parent element is usually more performant than binding event handlers to a bunch of different DOM elements. It looks like your binding events to elements in a loop, this is a bad practice. I just wanted to point that out too.
Here is a code example:
var cmp = Ext.getCmp('someComponentId');
cmp.on('click', function(me, event) {
if (event.currentTarget.tagName == "LI") {
// do something since the <li/> tag was clicked.
// event.currentTarget will be the <li/> DOM element,
// feel free to do with it as you please :)
}
}
I did as below in my case.
Below is the sample html code of div with li s.
<div class="menu1" id="menu1">
<ul>
<li id="students_tab">Students</li>
<li id="emps_tab">Employees</li>
</ul>
</div>
And below is the extjs code to add click event to li element.
<script type="text/javascript">
Ext.onReady(function(){
var tabs= Ext.query("li", "menu1");
Ext.each(tabs, function(item){
var el = Ext.get(item);
el.on("click", function(){
var tabName = this.id.substr(0, this.id.indexOf("_"));
alert("U have clicked on "+ tabName + " tab");
});
});
});
</script>

Resources