Adding events to button doesn't work with backbone - jquery-mobile

I read over 20 different articles and forum topics about that, tried different solutions but I didn't cope with it.
The following code doesn't work. I need someone's help...
LoginView.js
var LoginView = Backbone.View.extend({
//el: $('#page-login'),
initialize: function() {
_.bindAll(this, 'gotoLogin', 'render');
//this.render();
},
events: {
'click #button-login': 'gotoLogin'
},
gotoLogin : function(e){
e.preventDefault();
$('#signup-or-login').hide();
$('#login').show();
return true;
}
});
login.html
<div data-role="page" id="page-login">
<!-- SignUp or Login section-->
<div id="signup-or-login" data-theme="a">
<a data-role="button" data-theme="b" id="button-signup"> Sign Up </a>
<a data-role="button" data-theme="x" id="button-login"> Login </a>
</div>
<!-- Login section-->
<div id="login" data-theme="a">
<button data-theme="b"> Login </button>
<button data-theme="x"> Cancel </button>
</div>
</div>
The page is created in method of Backbone.Router extended class.
loadPage('login.html', new LoginView());

From what I understand, $.mobile.loadPage() grabs the desired html and attaches it to the DOM.
Currently, you're trying to set el after the View has been instantiated.
However, notice that Backbone.View attaches el and $el when it's instantiated:
var View = Backbone.View = function(options) {
...
this._ensureElement();
this.initialize.apply(this, arguments);
this.delegateEvents();
};
Also notice that View.setElement() sets $el by passing a selector or a jQuery objected to View.el:
setElement: function(element, delegate) {
if (this.$el) this.undelegateEvents();
this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
this.el = this.$el[0];
if (delegate !== false) this.delegateEvents();
return this;
}
Bottom line:
You need to set el (in your case with the provided jQuery object) while instantiating it:
// Where `view` is a reference to the constructor, not an instantiated object
var loadPage = function(url, view) {
$.mobile.loadPage(url, true).done(function (absUrul, options, page) {
var v,
pageId = page.attr('id');
v = new view({
el: page
});
...
}
}
You now call loadPage() like so:
loadPage('login.html', LoginView);
This gives Backbone.View the $el which to delegate your events.

Related

jQuery mobile iscroll view : Cannot type in text box properly

In my jquery mobile ios phonegap application, i use iscroll. On using iscroll, text boxes behave weirdly (move up and down on entering each character).
I enabled iscroll in my app by,
Added following scripts:
<script src="js/iscroll.js"></script>
<script src="js/jquery.mobile.iscrollview.js"></script>
My page looks like,
<div data-role="page" id="index">
<div data-theme="a" data-role="header" data-position="fixed" data-id="footer" data-tap-toggle="false" data-transition="none">
</div>
<div data-role="content" data-iscroll>
// following text field works weirdly
<input id="txtComment" placeholder="COMMENTS" value="" type="text" data-theme="b"/>
</div>
<div data-role="navbar" data-position="fixed" data-theme="a" data-id="footer" data-tap-toggle="false" data-transition="none">
</div>
</div>
I tried adding the following code, but didn't work
var selectField = document.getElementById('txtComment');
selectField.addEventListener('touchstart', function(e) {
e.stopPropagation();
}, false);
How can i fix it?
Please help.
Use this function with iscroll you can type in form fields.....
<script type="text/javascript">
var myScroll;
function loaded() {
myScroll = new iScroll('wrapper', {
useTransform: false,
onBeforeScrollStart: function (e) {
var target = e.target;
while (target.nodeType != 1) target = target.parentNode;
if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA')
e.preventDefault();
}
});
}
document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
document.addEventListener('DOMContentLoaded', loaded, false);
</script>
Try:
in the Android Manifest activity: android:windowSoftInputMode = "adjustNothing"
same problem raised for me.just refresh your scroll using
setTimeout(function ()
{
myScroll.refresh();
}, 1000);
by adding refresh at correct place you can overcome this problem.i have gone through many websites and tried a lot.its work me perfectly.
if it doesn't works then its Mobile version(android,ios) problem because scroll deosn't support.
hope it will helps u.

Listview style issue when changing the page

I'm working on a single page application using jquery mobile along with knockout.js binding on MVC4 platform..
This is my button on the main div page:
<div data-role="page" id="pageMain">
<div data-role="content">
View Invoices
</div>
</div>
This is my target div page:
<div data-role="page" id="pageExisting">
<div data-role="header">
<h1>Existing Invoices's</h1>
<a data-rel="back" data-role="button">Back</a>
</div>
<div class="choice_list" data-role="content" data-bind="visible: Headers().length > 0">
<ul id="headersList" data-role="listview" data-bind="foreach: Headers" data-filter-placeholder="Search Invoice" data-filter-theme="a" data-inset="true"
data-filter="true" data-theme="b">
<li>
<a href="#" data-inline="true">
<h2>Invoice No.: <span data-bind="text: inv_no"></span></h2>
<p>Amount.: <span data-bind="text: inv_amt"></span></p>
</a>
</ul>
</div>
</div>
Here is the script section:
var HeaderViewModel = function () {
//Make the self as 'this' reference
var self = this;
self.Headers = ko.observableArray([]);
function GetHeaders() {
//Ajax Call Get All Employee Records
// self.GetHeaders = function () {
$.ajax({
type: "GET",
url: "/api/InvAPI",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
self.Headers(data);
},
complete: function () {
$.mobile.changePage("#pageExisting");
},
error: function (error) {
alert(error.status + "<--and--> " + error.statusText);
}
});
}
self.GetHeader = function () {
GetHeaders();
}
};
$(document).ready(function () {
ko.applyBindings(new HeaderViewModel());
}
When I hit the button "view Invoices" I get a typical Jquery mobile formated listview
but the problem is when I hit the back button and navigate again to the "PageExisiting" div Page I get the list data with no styling..
When viewing page source in both cases, I noticed that on second navigation;
Li tags have not attributes.
I have tried some solutions like: listview refresh, page destroy, page create prior to the line:
$.mobile.changePage("#pageExisting");
with no luck.
I'm stuck here guys and I will appreciate your proposed solutions
Thanks
Refresh list view before showing the page.
$('#pageID').on('pagebeforeshow', function() {
$('[data-role=listview]').listview('refresh');
});

Backbone.js View not showing in browser after render call

JS code below
Model
var EntryName = Backbone.Model.extend({
defaults : {
name : ""
},
});
Model Collection
var EntryNames = Backbone.Collection.extend({
model : EntryName,
initialize : function() {
}
});
ModelView
var EntryNameView = Backbone.View.extend({
tagName : 'li',
// Cache the template function for a single item.
entrynametpl : _.template('<li><a href="#" ></a></li>'),
// Re-render.
Render function
render : function() {
this.$el.html(this.entrynametpl(this.model.toJSON()));
return this;
},
});
ModelCollectionView
var EntryNamesView = Backbone.View.extend({
// tagName: "ul",
// className: "nav-search",
el : $('#entriestree'),
initialize : function() {
//this.template = _.template($('#entries-template').html());
_.bindAll(this, 'render');
},
Render function
render : function() {
var item, self = this;
//var template = $("#item-template");
this.collection.each(function(entry) {
item = new EntryNameView({
model : entry
});
self.$el.append(item.render().el);
});
console.log($(this.el));
return this;
}
});
Model Collection
Model Collection This is where and how i call render.
function onDeviceReady()
{
// console.log("Opening panel");
$("#nav-panel").panel( "open");
console.log("creating collection");
var donuts = new EntryNames();
donuts.reset([ {"name" : "Boston Cream"}, {"name" : "Lemon-Filled"}, {"name" : "Rusty Iron Shavings"}]);
console.log("created collection");
var donutCollectionView = new EntryNamesView({collection : donuts});
donutCollectionView.render();
$("#nav-panel" ).trigger( "updatelayout" );
}
Model Collection
The HTML code is below Model Collection
<body>
<div id="panel-fixed-page1" class="jqm-demos ui-responsive-panel"
data-url="panel-fixed-page1" data-role="page">
<div data-role="header" data-theme="f" data-position="fixed">
<div data-role="navbar" data-grid="d">
<ul>
<li><a class="ui-btn-active" href="#" data- theme="a">Entry</a></li>
<li>Addresses</li>
<li>Attachments</li>
<li>Delivery Collection</li>
</ul>
</div>
<!-- /navbar -->
Menu
</div>
<!-- /header -->
<div class="jqm-content" data-role="content">
</div>
<!-- /content -->
<div id="nav-panel" data-role="panel"
data-position-fixed="true">
<li><a href="#" data-rel="close" >INBOX</a></li>
<ul id="entriestree" class="nav-search" data-role="listview" data- theme="a">
</ul>
</div>
</div>
</body>
Model Collection
Model Collection
In your EntryNamesView.render, this is not pointing to the view, because it is inside each callback function scope. Try change it to use a self:
render : function() {
var item, self = this;
//var template = $("#item-template");
this.collection.each(function(entry) {
item = new EntryNameView({
model : entry
});
self.$el.append(item.render().el);
});
console.log($(this.el));
return this;
}
Your _.underscore template has no placeholder variables:
entrynametpl : _.template('<li><a href="#" ></a></li>') //renders empty li's
correct this to:
entrynametpl: _.template('<li><%= name %></li>')
okay i figured out the issue, I set the view's element "el" property outside of the constructor (the initialize function), so it was never getting set with the proper dom element because the dom element came after the script reference in the html file. once i moved the script import to the end of the page, voila life is good..silly mistakes cost you sooo much time!!!

backbone view passed to jQuery Mobile

I've been trying to use backbonejs and jqm together.
I can render the main page alright. The page has a list that the user can tap on. The item selected should show a detail page with info on the list item selected. The detail page is a backbone view with a template that's rendered in the item's view object.
The detail's view .render() produces the html ok and I set the html of the div tag of the main page to the rendered item's detail markup. It looks like this:
podClicked: function (event) {
console.log("PodListItemView: got click from:" + event.target.innerHTML + " id:" + (this.model.get("id") ? this.model.get("id") : "no id assigned") + "\n\t CID:" + this.model.cid);
var detailView = new PodDetailView({ model: this.model });
detailView.render();
},
The detail view's render looks like this:
render: function () {
this.$el.html(this.template({ podId: this.model.get("podId"), isAbout_Name: this.model.get("isAbout_Name"), happenedOn: this.model.get("happenedOn") }));
var appPageHtml = $(app.el).html($(this.el));
$.mobile.changePage(""); // <-- vague stab in the dark to try to get JQM to do something. I've also tried $.mobile.changePage(appPageHtml).
console.log("PodDetailView: render");
return this;
}
I can see that the detail's view has been rendered on the page by checking Chrome's dev tools html editor but it's not displaying on the page. All I see is a blank page.
I've tried $.mobile.changePage() but, without an URL it throws an error.
How do I get JQM to apply it's class tags to the rendered html?
the HTML and templates look like this:
<!-- Main Page -->
<div id="lessa-app" class="meditator-image" data-role="page"></div>
<!-- The rest are templates processed through underscore -->
<script id="app-main-template" type="text/template">
<div data-role="header">
<h1>#ViewBag.Title</h1>
</div>
<!-- /header -->
<div id="main-content" data-role="content">
<div id="pod-list" data-theme="a">
<ul data-role="listview" >
</ul>
</div>
</div>
<div id="main-footer" data-role='footer'>
<div id="newPod" class="ez-icon-plus"></div>
</div>
</script>
<script id="poditem-template" type="text/template">
<span class="pod-listitem"><%= isAbout_Name %></span> <span class='pod-listitem ui-li-aside'><%= happenedOn %></span> <span class='pod-listitem ui-li-count'>5</span>
</script>
<script id="page-pod-detail-template" type="text/template">
<div data-role="header">
<h1>Pod Details</h1>
</div>
<div data-role="content">
<div id='podDetailForm'>
<fieldset data-role="fieldcontain">
<legend>PodDto</legend>
<label for="happenedOn">This was on:</label>
<input type="date" name="name" id="happenedOn" value="<%= happenedOn %>" />
</fieldset>
</div>
<button id="backToList" data-inline="false">Back to list</button>
</div>
<div data-role='footer'></div>
</script>
Thanks in advance for any advice... is this even doable?
I've finally found a way to do this. My original code has several impediments to the success of this process.
The first thing to do is to intercept jquerymobile's (v.1.2.0) changePage event like this:
(I've adapted the outline from jqm's docs and left in the helpful comments: see http://jquerymobile.com/demos/1.2.0/docs/pages/page-dynamic.html
)
$(document).bind("pagebeforechange", function (e, data) {
// We only want to handle changePage() calls where the caller is
// asking us to load a page by URL.
if (typeof data.toPage === "string") {
// We are being asked to load a page by URL, but we only
// want to handle URLs that request the data for a specific
// category.
var u = $.mobile.path.parseUrl(data.toPage),
re = /^#/;
// don't intercept urls to the main page allow them to be managed by JQM
if (u.hash != "#lessa-app" && u.hash.search(re) !== -1) {
// We're being asked to display the items for a specific category.
// Call our internal method that builds the content for the category
// on the fly based on our in-memory category data structure.
showItemDetail(u, data.options); // <--- handle backbone view.render calls in this function
// Make sure to tell changePage() we've handled this call so it doesn't
// have to do anything.
e.preventDefault();
}
}
});
The changePage() call is made in the item's list backbone view events declaration which passes to the podClicked method as follows:
var PodListItemView = Backbone.View.extend({
tagName: 'li', // name of (orphan) root tag in this.el
attributes: { 'class': 'pod-listitem' },
// Caches the templates for the view
listTemplate: _.template($('#poditem-template').html()),
events: {
"click .pod-listitem": "podClicked"
},
initialize: function () {
this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this);
},
render: function () {
this.$el.html(this.listTemplate({ podId: this.model.get("podId"), isAbout_Name: this.model.get("isAbout_Name"), happenedOn: this.model.get("happenedOn") }));
return this;
},
podClicked: function (event) {
$.mobile.changePage("#pod-detail-page?CID='" + this.model.cid + "'");
},
clear: function () {
this.model.clear();
}
});
In the 'showItemDetail' function the query portion of the url is parsed for the CID of the item's backbone model. Again I've adapted the code provided in the jquerymobile.com's link shown above.
Qestion: I have still figuring out whether it's better to have the code in showItemDetail() be inside the view's render() method. Having a defined function seems to detract from backbone's architecture model. On the other hand, having the render() function know about calling JQM changePage seems to violate the principle of 'separation of concerns'. Can anyone provide some insight and guidance?
// the passed url looks like #pod-detail-page?CID='c2'
function showItemDetail(urlObj, options) {
// Get the object that represents the item selected from the url
var pageSelector = urlObj.hash.replace(/\?.*$/, "");
var podCid = urlObj.hash.replace(/^.*\?CID=/, "").replace(/'/g, "");
var $page = $(pageSelector),
// Get the header for the page.
$header = $page.children(":jqmData(role=header)"),
// Get the content area element for the page.
$content = $page.children(":jqmData(role=content)");
// The markup we are going to inject into the content area of the page.
// retrieve the selected pod from the podList by Cid
var selectedPod = podList.getByCid(podCid);
// Find the h1 element in our header and inject the name of the item into it
var headerText = selectedPod.get("isAbout_Name");
$header.html("h1").html(headerText);
// Inject the item info into the content element
var view = new PodDetailView({ model: selectedPod });
var viewElHtml = view.render().$el.html();
$content.html(viewElHtml);
$page.page();
// Enhance the listview we just injected.
var fieldContain = $content.find(":jqmData(role=listview)");
fieldContain.listview();
// We don't want the data-url of the page we just modified
// to be the url that shows up in the browser's location field,
// so set the dataUrl option to the URL for the category
// we just loaded.
options.dataUrl = urlObj.href;
// Now call changePage() and tell it to switch to
// the page we just modified.
$.mobile.changePage($page, options);
}
So the above provides the event plumbing.
The other problem I had was that the page was not set up correctly. It's better to put the page framework in the main html and not put it in an underscore template to be rendered at a later time. I presume that avoids issues where the html is not present when jqm takes over.
<!-- Main Page -->
<div id="lessa-app" data-role="page">
<div data-role="header">
<h1></h1>
</div>
<!-- /header -->
<div id="main-content" data-role="content">
<div id="pod-list" data-theme="a">
<ul data-role="listview">
</ul>
</div>
</div>
<div id="main-footer" data-role='footer'>
<div id="main-newPod" class="ez-icon-plus"></div>
</div>
</div>
<!-- detail page -->
<div id="pod-detail-page" data-role="page">
<div data-role="header">
<h1></h1>
</div>
<div id="detail-content" data-role="content">
<div id="pod-detail" data-theme="a">
</div>
</div>
<div id="detail-footer" data-role='footer'>
back
</div>
</div>

Expressions are not evaluated in $watch of custom directive of angularjs

I have a below custom directive in angularjs which uses model thats gets updated from server,
I have added a watch listener to watch the changes of that model,
var linkFn;
linkFn = function(scope, element, attrs) {
scope.$watch('$parent.photogallery', function(newValue, oldValue) {
if(angular.isUndefined(newValue)) {
return;
}
var $container = element;
alert($container.element);
$container.imagesLoaded(function() {
$container.masonry({
itemSelector : '.box'
});
});
});
};
return {
templateUrl:'templates/Photos_Masonry.htm',
replace: false,
transclude:true,
scope: {
photogallery: '=photoGallery',
},
restrict: 'A',
link: linkFn
However, when i debug in my watch directive, i still see that expressions in templates are still unresolved.i.e. photo.label, ng-src all are still unresolved. AFIK, $digest would be called only after $eval. Is this intended behavior?
My jQuery calls are not working due to this? Is there any other event where i get the result element with evaluated expressions?
Here is my template, which has ng-repeat in it,
<div id="container" class="clearfix">
<div class="box col2" ng-repeat="photo in photogallery">
<a ng-href="#/viewphotos?id={{photo.uniqueid}}&&galleryid={{galleryid}}"
title="{{photo.label}}"><img
ng-src="{{photo.thumbnail_url}}" alt="Stanley" class="fade_spot" /></a>
<h3>
<span style="border-bottom: 1px solid black;font-weight:normal;font-size:14px;">{{galleryname}}</span>
</h3>
<h3>
<span style="color:#20ACB8;font-weight:normal;font-size:17px;">{{photo.seasonname}}</span>
</h3>
</div>
</div>
photogallery is initialized in parent controller,
function MyCtrlCampaign($scope, srvgallery, mygallery) {
$scope.updatedata = function() {
$scope.photogallery = srvgallery.getphotos($routeParams);
};
$scope.getphotos = function() {
srvgallery.photos().success(function(data) {
$scope.updatedata();
}).error(function(data) {
});
};
Directive is used in below way,
<div masonry photo-gallery="photogallery" >
</div>
Kindly let me know your views on this.
Looks like this has been resolved in your Github issue (posted for the convenience of others).

Resources