Firing event click works on Browser but not on IOS app - jquery-mobile

I'm currently developing an app with Backbone.js, underscore.js and jQuery Mobile. It's a "quiz" with several questions, and when users tap the answer, it goes to next question.
It works kindly fine on browser, but I want to develop an IOS app using Phonegap, and (after some little changes) everything seems to go ok, but when I click some answer, the event (neither click, nor tap) don't fire. I tried it with a "submit" button, and it worked, so there's something strange there :(
Relevant code:
var quizView = Backbone.View.extend({
collection: new quizCollection(),
render: function() {
(...)
},
events: {
'click .resp': 'saveStatus', // original
'tap .resp': 'saveStatus', // and maybe...?
'touch .resp': 'saveStatus'// what if...?
},
saveStatus: function(event) {
var el = $(event.currentTarget).siblings("input");
var score = (el.val() == questions.selected.correcta) ? (1) : (0);
datosPartida.preguntasResp++;
datosPartida.score += score;
this.collection.showQuestion();
}
});
Where ".resp" is the class in the label of radio input.
The code of the template is:
<div data-role="header">
<h1>Pregunta</h1>
</div>
<div data-role="content">
<div>
<%
var id = preguntas.id;
var corr = preguntas.correcta;
var it = 0;
%>
<h3><%= preguntas.pregunta %></h3>
<% _.each(preguntas.respuestas, function(respuesta) { %>
<input id="respuesta_<%= it %>" name="respuesta_<%= id %>" type="radio" value="<%= respuesta.id %>">
<label data-role="button" class="resp" for="respuesta_<%= it %>">
<%= respuesta.respuesta %>
</label>
<% it++; %>
<% }); %>
<!-- <button data-role="button" class="sigpreg">Siguiente</button> -->
</div>
</div>
Why is my tap/click not firing the event on IOS, but it does on browser?
I hope some help, thanks in advance.
Finally i found the answer here.
Set the event touchstart and it works fine :)

Related

How do I properly handle submit events in Vue + Rails?

I'm learning Rails, Vue and JS and I've got a question on the proper handling of submit buttons on Rails using Vue component to validate it. I'm using mdbootstrap for the styles.
I built a form wizard which uses vee-validate for validating the fields and in some forms I want to perform some server side operations too (eg.: validate exact address with geocoding). I'm currently facing basically three issues.
Although I added a v-clock directive, I'm still seeing a little
flicker every time the form wizard component gets loaded (eg.: page
refresh).
I had to workaround the Rails automatic data-disable-with handling to get it working, and it looks not optimal to me, and I'd like to know if there's a better way to deal with it (I had to disable the submit event propagation and prevent default and do the disabling/enabling manually otherwise the handler from Rails UJS will receive it afterwards and disable the button forever).
Although the button gets enabled again, it gets brighter every time I click on it if validation fails (some handler from mdbootstrap maybe?). It happens only after I click on refresh button on the browser and I've noticed the following div is created after each click followed by an error during form validation, causing the button to become "brighter" as in a accumulated "disabled effect":
Anyone has ideas on how these issues could be solved? Thanks!
new.html.erb:
<div id="stepper">
<div v-cloak>
<transition-group name="fade">
<div class="d-none d-lg-block" key="progress_bar">
<%= render 'forms/progress_bar' %>
</div>
<div id="step1" v-if="step === 1" key="step1">
<%= render 'forms/description' %>
</div>
<div id="step2" v-if="step === 2" key="step2">
<%= render 'forms/address' %>
</div>
</transition-group>
</div>
</div>
_description.html.erb:
<template>
<form
id="description-form"
data-vv-scope="description-form"
novalidate="true"
#submit.prevent="next('description-form', $event)">
<div class="row mb-5">
<div class="col-lg-12 col-md-12">
<div class="container">
<div class="row" id="step-1">
<div class="col-lg-6">
<div class="max-height-80">
<div class="mb-4">
<h4><%= t(:'step1.title') %></h4>
</div>
<div class="form-group">
<label
for="name"
class="control-label">
<%= t(:'step1.label.name') %>
</label>
<input
id="name"
name="name"
type="text"
class="form-control"
placeholder="<%= t(:'step1.input.name') %>"
v-validate="'required'"
v-model="name"
:class="{ 'is-invalid': errors.has('name','description-form') }"
required/>
<div
v-if="errors.has('name','description-form')"
class="invalid-feedback">
<%= t(:'name.required') %>
</div>
</div>
<div class="form-group">
<label
for="description"
class="control-label">
<%= t(:'step1.label.description') %>
</label>
<textarea
id="description"
name="description"
class="form-control"
placeholder="<%= t(:'step1.input.description') %>"
rows="11"
v-validate="'required'"
v-model="description"
:class="{ 'is-invalid': errors.has('description','description-form') }"
required>
</textarea>
<div
v-if="errors.has('description','description-form')"
class="invalid-feedback">
<%= t(:'description.required') %>
</div>
</div>
</div>
<footer class="page-footer white fixed-bottom d-block d-sm-none z-depth-1" id="footer">
<div class="d-flex justify-content-end">
<button class="btn btn-default pull-right" type="submit" data-remote="true" data-disable-with="<%= wait_spinner %>"><%= t(:'btn.next') %></button>
</div>
</footer>
<div class="d-none d-sm-block">
<div class="d-flex justify-content-end mt-2">
<button class="btn btn-default pull-right" type="submit" data-remote="true" data-disable-with="<%= wait_spinner %>"><%= t(:'btn.next') %></button>
</div>
</div>
</div>
<div class="col-lg-2"></div>
<div class="col-lg-4 d-none d-lg-block mt-lg-5">
</div>
</div>
</div>
</div>
</div>
</form>
</template>
stepper.js
var element = document.getElementById("stepper");
if (element != null) {
Vue.use(VeeValidate);
Vue.use(VueResource);
Vue.http.headers.common['X-CSRF-Token'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const stepper = new Vue({
el: element,
data() {
return {
/**
* The step number (starting from 1).
* #type {Integer}
*/
step:1,
name:null,
description:null
}
}
},
methods: {
/**
* Goes back to previous stepp
*/
prev() {
this.step--;
},
/**
* Triggers validation of current step and goes to the
* next step if validation succeeds
* #param {String} scope The step scope used for validation.
* #param {Object} Event that triggered the next step (form submit)
*/
next(scope, event) {
if (event != undefined) {
event.stopImmediatePropagation();
event.preventDefault();
event.stopPropagation();
}
const form = event.currentTarget;
$("button[type=submit]",form).each(function() {
Rails.disableElement(this);
});
this.validateFields(scope, event);
},
validateFields(scope, event) {
this.$validator.validateAll(scope).then(function (valid) {
this.postFieldsValidation(valid, event);
}.bind(this));
},
postFieldsValidation(valid, event) {
if (valid) {
stepper.step++;
}
const form = event.currentTarget;
$("button[type=submit]",form).each(function() {
Rails.enableElement(this);
});
},
handleError(error) {
alert(error)
},
submit() {
}
}
});
}
* edit *
for #3, I'm using a workaround for removing the div with waves-ripple class inside my button when re-enabling elements on the form.
$("div").remove("button .waves-ripple");
however, it would be nice to know the root cause for it.

TypeError: Cannot set property 'chat_room_id' of undefined when using ng-if

I'm running into a bit of a road block here.
I'm working on a chat feature, which is currently within a rails partial in the application.html.erb file.
What I'm looking to do is have a list of a user's friends display in the chat area initially. When the user clicks on a friend's name, the corresponding chat room opens and the messages between the user and friend are displayed. If the user wishes to exit the chat, and view his friends list again, the user would simply click a button (currently "View Friends").
I am currently toggling between friends and rooms/messages using the ng-if directive.
I have not completely set this up yet, so I know there are bugs. I have created user friendships within rails, set up my REST API in rails, and can GET and POST resources via Angular and Restangular.
However, the issue at hand is that ever since I implemented the ng-if directives in the partial, I am getting the following error:
TypeError: Cannot set property 'chat_room_id' of undefined
If I am to remove the ng-if directives and all of the corresponding $scope.messagesVisible and $scope.friendsVisible variable references within the controller, the form submit works, so I know it has something to do with my implementation of the ng-if directives, and the fact that newMessage is undefined, but I'm not sure why.
I suspect it has something to do with Angular promises or my lack of understanding in regards to both Angular promises and the ng-if directive, but if anyone could shed some light on why this may be happening (and offer a solution) that would be amazing.
Thanks, guys!
Code below (please excuse terrible styling - it's a placeholder)
rails_partial:
<section ng-app="atmosphere" ng-controller="AtmoChatCtrl" style="position:relative; top:32%; float:right; right:5px; margin-bottom:10px; margin-top:30px;">
<div ng-if="friendsVisible" style="position:relative; left:35px; width:100px;">
<% user_friendships.each do |friendship| %>
<ul>
<% friend = friendship.friend %>
<% if friendship.accepted? %>
<li style="cursor:pointer;" ng-click="setChatAttributes(<%= friend.id %>, <%= current_user.id %> );"><%= friend.name %></li>
</ul>
<% end %>
<% end %>
</div>
<div ng-if="messagesVisible" style="position:absolute; top:60%; width: 150px; height:40%; right:-80px; margin-top:100px">
<div>
<p ng-repeat="message in messages">{{message.user_id}}: {{message.body}}</p>
<form ng-submit="saveMessage();">
<input type="text" ng-model="newMessage.body">
</form>
</div>
</div>
<div style="position:absolute; top:300px;">
<button ng-if="messagesVisible" ng-click="viewFriends();">View Friends</button>
</div>
</section>
<%= javascript_include_tag "angular/application" %>
<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular-resource.min.js" %>
angular_controller:
angular.module('AtmoChatCtrl', [])
.controller('AtmoChatCtrl', ['$scope', '$resource', '$interval','Pusher','Restangular', function ($scope, $resource, $interval, Pusher, Restangular) {
$scope.baseMessages = Restangular.one('api/chat_rooms', 2).all('chat_messages');
$scope.roomId = '2';
$scope.messagesVisible = false;
$scope.friendsVisible = true;
console.log("SET ROOM");
$interval(function(){
$scope.baseMessages.getList().then(function(messages) {
$scope.messages = messages;
});
console.log("POLLING");
}, 1000);
$scope.saveMessage = function() {
$scope.newMessage.chat_room_id = $scope.roomId;
$scope.newMessage.user_id = $scope.userId;
$scope.baseMessages.post($scope.newMessage).then(function(newMessage){
$scope.messages.push(newMessage);
console.log("SAVED");
})
}
$scope.setChatAttributes = function(roomId, userId) {
$scope.baseMessages = Restangular.one('api/chat_rooms', roomId).all('chat_messages');
$scope.roomId = roomId;
$scope.userId = userId;
$scope.messagesVisible = true;
$scope.friendsVisible = false;
console.log(roomId)
console.log(userId)
}
$scope.viewFriends = function() {
$scope.friendsVisible = true;
$scope.messagesVisible = false;
}
}]);
NVM. I'm an idiot. Solved by setting $scope.newMessage = {}; after the first $scope.friendsVisible = true;

select2 4.0 - TypeError: $(...).select2(...).select2(...) is undefined

Trying to finally change over to the 4.0 version of select2 and running into a problem.
All of this works perfectly fine on 3.5. On button click I open a bootstrap modal and load a remote page into it. I have tested all everything on a normal page (not in a modal) and it works correctly. When loaded in a modal as below the modal opens and everything like it always had for 3.5, but select2 is returning an error. I believe the issue here is the select2 is being loaded from a remote page... thing is this same remote loading method always worked flawlessly with 3.5.
TypeError: $(...).select2(...).select2(...) is undefined
js:
// show edit modal
$('#datatable2').on('click', '.dtEdit', function () {
var data = {
'filter_id': $(this).parents('tr').attr('id').replace('dtrow_', '')
};
$('#modal-ajax').load(
'/modals/m_filtering_applications_filters.php',
data,
function() {
$(this).modal('show');
changeSettings();
hourSelection();
}
);
});
// change filter modal confirmation
var changeSettings = function() {
// get the default filter
var default_filter = $("#filter_default").val();
//app list
$("#vedit-filter").select2({
placeholder: {
id: default_filter, // or whatever the placeholder value is
text: default_filter // the text to display as the placeholder
},
allowClear: true,
multiple: false,
tags: true,
createTag: function (query) {
return {
id: query.term,
text: query.term,
tag: true
}
},
ajax: {
dataType: 'json',
delay: 500,
type: 'post',
url: '/process/get_application_list.php',
data: function (params) {
return {
term: params.term, // search term
page: params.page, //page number
page_limit: 25, // page size
};
},
results: function (data, page) {
var more = (page * 25) < data.total; // whether or not there are more results available
return {
results: data.results,
more: more
};
}
}
}).select2('val', [default_filter]).on('change', function() {
$(this).valid();
});
}
m_filtering_applications_filters.php :
Just the contents of the modal which is loaded in :
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"></button>
<h3 class="modal-title">Change these settings?</h3>
</div>
<form id="application-filters-edit">
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<div class="row">
<div class="col-md-12 margin-bottom-30 form-group">
<div class="input-modal-group">
<label for="vedit-filter" class="f-14"><b>Application to filter :</b></label>
<select id="vedit-filter" name="settings[filter]" class="form-control select2">
<option value="<?php echo htmlspecialchars($result['filter'], ENT_QUOTES, 'UTF-8'); ?>" selected=""><?php echo htmlspecialchars($result['filter'], ENT_QUOTES, 'UTF-8'); ?></option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<input type="hidden" name="settings[users][]" value="<?php echo $result['user_id']; ?>"/>
<input id="filter_default" type="hidden" name="settings[original]" value="<?php echo htmlspecialchars($result['filter'], ENT_QUOTES, 'UTF-8'); ?>"/>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<?php
if (!$result)
{
//disable the button
echo '<button type="button" class="btn btn-primary disabled" data-dismiss="modal"><i class="fa fa-check-circle"></i> Save Settings</button>';
}
else
{
// show the button
echo '<button class="btn btn-primary" type="submit"><i class="fa fa-check-circle"></i> Save Settings</button>';
}
?>
</div>
</form>
</div>
</div>
UPDATE:
Okay, the error is coming from :
}).select2('val', [default_filter]).on('change', function() {
$(this).valid();
});
attached to the end... this is for using the jquery validation script (I did not include this in the jS here), and again, worked fine in 3.5. Can you not add on to the select2() anymore with 4.0 or something?
When removing this line the error goes away, but the display of the select2 is very small in width and I cannot gain focus on the search box to enter any values so it is still unusable within the modal.
UPDATE2:
Realized the events changed with 4.0 so this seems to work :
}).on("change", function (e) {
$(this).valid();
});
Still cannot enter anything into the search box though. Also, I notice if I click on anything other than the arrow in the input box to show the dropdown it acts as if the mouse is being held down - it highlights all the text/content within the modal.
Solution : All the issues I was having with the select2 in my bs3 modal were solved by adding the following in my js :
$.fn.modal.Constructor.prototype.enforceFocus = $.noop;
The highlighting of content/text is gone. The focus on the search box is gone. For now everything seems to work great. I do not have any information as to what this line actually does at the moment, but will be researching this to find out and will add it back here for future reference for anyone.

jquery mobile multiple page, tinyscrollbar missing bar

I try to implement tinyscrollbar (tinyscrollbar.js) into jquery mobile with data-role=page. On the first page it works perfectly fine. but on the second page, the bar is missing. until i clicked on inspect element. I tried tinyscrollbar_update(), update(0), slideToggle().promise()... non works. They all give me different kind of errors.
looks like this
<div class="scrollbar1">
<div class="scrollbar" style="height: 200px;">
<div class="track" style="height: 200px;">
<div class="thumb" style="top: 0px; height: 32.6530612244898px;">
<div class="end"></div>
</div>
</div>
</div>
<div class="viewport">
<div class="overview">//text</div>
</div>
<div id="footerNavigation">
Back
Next
</div>
i copied the exact same code as above to my second page code:
<div id="theNextPage" data-role="page"><!--scrollbar code--></div>
and my javascript file:
$(document).ready(function() {
$('.scrollbar1').tinyscrollbar();
});
anybody has any idea how is that so? Any way to solve it?
I have found out away!
change the class into id.
class="scrollbar1" >> id="scrollbar1"
next page:
class="scrollbar2" >> id="scrollbar2"
$(document).ready(function() {
$('#scrollbar1').tinyscrollbar();
$('#scrollbar2').tinyscrollbar();
});
$(document).on("pageshow", "#theNextPage", function () {
var $scrollbar2 = $('#scrollbar2');
$scrollbar2.tinyscrollbar();
var scrollbar1 = $scrollbar2.data("plugin_tinyscrollbar")
$scrollbar2.update(0);
});
like this would be better

Cannot get jqueryui tabs to work properly in Ember view

I'm trying to run up a little prototype in Ember.JS at the moment with a view to completely re-writing the UI of a web application as an Ember Application running against a WebAPI, but although I've managed to get Ember running OK, I cannot get jqueryui to initialise the tabs correctly.
It seems to work fine if within the view I put static data for tabs to be created from, but if I'm using dynamic data then it just doesn't work.
I have an Ember view template
<script type="text/x-handlebars" id="index">
<div id="tabs" class="ui-tabs">
<ul>
{{#each model}}
<li>
<span class="ui-icon ui-icon-person"></span>
<a {{bindAttr href="route"}} {{bindAttr title="tabTitle"}}><span>{{title}}</span></a>
</li>
{{/each}}
</ul>
{{#each model}}
<div {{bindAttr id="tabTitle"}}>
<p>
Retrieving Data - {{title}}
</p>
</div>
{{/each}}
</div>
</script>
and a view
App.IndexView = Ember.View.extend({
templateName: 'index',
didInsertElement: function () {
var tabs = $("#tabs").tabs();
}
});
and a model
App.Section = DS.Model.extend({
name: DS.attr('string'),
title: DS.attr('string'),
tabTitle: function () {
return 'tab-' + this.get('name');
}.property("name"),
route: function () {
return '#' + this.get('tabTitle');
}.property("tabTitle")
});
App.Section.FIXTURES = [
{
id: 1,
name: 'home',
title: 'Home'
},
{
id: 2,
name: 'users',
title: 'Users'
}
];
It appears to generate the HTML correctly (from checking in Firebug), but this does not work, where as if I replace the template with
<script type="text/x-handlebars" id="index">
<div id="tabs" class="ui-tabs">
<ul>
<li>
<span class="ui-icon ui-icon-person"></span>
<span>Home</span>
</li>
<li>
<span class="ui-icon ui-icon-person"></span>
<span>Users</span>
</li>
</ul>
<div id="tab-home">
<p>
Retrieving Data - Home
</p>
</div>
<div id="tab-users">
<p>
Retrieving Data - Users
</p>
</div>
</div>
</script>
it works perfectly.
I'm assuming that it's something to do with the DOM not being completely rendered by the time the tabs are initialised, but everything I can find says that didInsertElement is the place to do it, and I have had time to dig deeper yet.
I'd be grateful for any ideas.
Edit: I've managed to make this work in a fashion by doing the following:
App.IndexView = Ember.View.extend({
templateName: 'index',
didInsertElement: function () {
Ember.run.next(this, function () {
if (this.$('#tab-users').length > 0) {
var tabs = $('#tabs').tabs();
} else {
Ember.run.next(this.didInsertElement);
}
});
},
});
The problem with this is that 1) it requires me to know what one of the last elements that will be written to the view is called (and obviously with dynamic data I won't necessarily know that), so that I can keep checking for it, and 2) the inefficiency of this technique makes me want to scream!
In addition, we get a good old FoUC (Flash of Unstyled Content) after things have been rendered, but before we then get JQueryUI to style them correctly.
Any suggestions gratefully received.
It's still not nice... but this at least does work, and is reasonably efficient...
From Ember.js - Using a Handlebars helper to detect that a subview has rendered I discovered how to write a trigger, and because of the way that the run loop seems to work, inserting the trigger in the last loop on the page causes it to be called n times, but only after the loop is complete, so a quick state check "hasBeenTriggered" ensures that you only execute the delgate function once.
My code now looks like this:
<script type="text/x-handlebars" id="index">
<div id="tabs" class="ui-tabs">
<ul>
{{#each model}}
<li>
<span class="ui-icon ui-icon-person"></span>
<a {{bindAttr href="route"}} {{bindAttr title="tabTitle"}}><span>{{title}}</span></a>
</li>
{{/each}}
</ul>
{{#each model}}
<div {{bindAttr id="tabTitle"}}>
<p>
Retrieving Data - {{title}}
</p>
</div>
{{trigger "triggered"}}
{{/each}}
</div>
</script>
with the trigger
Ember.Handlebars.registerHelper('trigger', function (evtName, options) {
options = arguments[arguments.length - 1];
var hash = options.hash,
view = options.data.view,
target;
view = view.get('concreteView');
if (hash.target) {
target = Ember.Handlebars.get(this, hash.target, options);
} else {
target = view;
}
Ember.run.next(function () {
target.trigger(evtName);
});
});
and view
App.IndexView = Ember.View.extend({
templateName: 'index',
hasBeenTriggered: false,
triggered: function () {
if (!this.get("hasBeenTriggered")) {
var tabs = $('#tabs').tabs();
this.set("hasBeenTriggered", true);
}
}
});
I'd love to know if there's a better way of doing this, as this still doesn't get round the FOUC problem either (which again can be done with more JS hacks)... :(

Resources