JQuery Mobile - How can I pass data context from a listview item to another page? - jquery-mobile

I'm generating a listview of "products" dynamically with some JSON data I'm getting using ajax request. When an user clicks on a item I want to take them to another page where all of the products details would be displayed.
$(document).delegate("#store", "pageinit", function() {
getProducts('',
function(data){
let products = data.products;
storeProducts(products);
$(".product-list").html('');
$.each(products, function(i, item){
$(".product-list").append(`
<li>
<a href="#product">
<img src="${item.picture}" alt="">
<h2>${item.prod_name}</h2>
<p>${item.prod_desc}</p>
<small>Price: <strong>€${item.unit_price}</strong></small>
</a>
</li>
`).listview("refresh");
});
},
function(e){
console.log(e);
},
function(data){
console.log('always');
});
});
Now the problem is that I don't see how to pass the data context of a list item to another page (#product) in order to populate it with the corresponding information. I have seen a few approaches to similar use cases but they seem a bit ugly to me.
I believe this is a very basic feature for a web/mobile application nowadays; However JQuery Mobile doesn't seem to make it easy for a developer to implement such feature. Any plugin/library I can use to implement this?
Thanks in advance.

As you are creating the list items dynamically, this could be easily done by adding your own custom data attribute, like this:
'<li data-id="+${item.prod_id}+">'
Then, you can get the product context this way:
$(document).on("vclick", ".product-list li>a", function() {
var id = $(this).data("id");
...get the product context by id
});
Here is an example with working demo: https://stackoverflow.com/a/43915438/4845566
BTW, just two small hints aside :
if you create the html token by using [].join("") you will avoid the extra spaces/tabs in markup.
You can also refresh the listview just one time, out of the products loop.

Related

Retrieve passed data between pages with JQuery Mobile

I'm generating some list items with JQuery Mobile. These list items have an id on the anchor. All anchors needs to load the page event.html. I need to retrieve the id when event.html is loaded.
On my index page I have the following code, which should pass the id from my anchor and refer me to event.html.
$(document).on('pageinit', '#index', function() {
$(document).on('click', '#overview a', function(e) {
e.preventDefault();
$.mobile.changePage('event.html', {
transition: 'slide',
data: {
'id': 'this.id'
}
});
});
});
On the page event.html I want to retrieve the data id.
$(document).on('pageinit', '#event', function() {
//output data id
});
How do I do this?
I've seen some examples where people have tried to do this by using global variables and submitting through get request. I have not been able to successfully reproduce their solutions and I would prefer if the data could be passed directly from the pages. Is this possible?
i think get method is the best , however I sometimes use the method data() , but you have to create a dummy element that is common among pages to keep you data , you can check this page though :
http://api.jquery.com/data/

Counting clicks to external links with rails

I have Entry model with url field, which contains link to external site.
In view I list these links, and now I'd like to start counting when someone clicks it, and keep this info in database. What's the best way of doing it?
You can easily use google analytics to track outbound links: http://support.google.com/analytics/bin/answer.py?hl=en&answer=1136920
If that is not an option you will need to add some javascript to your links make an ajax request to the server to increment the count before transferring the user to the new url. Something similar to this jquery code:
$('a').click(function(){
var stored_ulr = $(this).attr('href');
$.ajax({
url: #your server url to increment count,
data: #data you need to send,
success: function() { window.location = stored_url; },
});
return false;
});
The above code is just a general outline. You will have to fill in the blanks and make it work for your needs.

jQuery Sortable freezes after AJAX html replace

jQuery Code: I removed all my Sortable settings. If you feel they are pertinent I can put them back in.
$('#navsort').sortable({ ... });
$("#content").on('click', '#submit_menu', function (event){
event.preventDefault();
$.ajax({
type: "POST",
url: "menu-ajax.php",
data: { data:$('#form').serialize() }
}).done(function (response) {
$('#content').hide().html(response).fadeIn('slow');
});
});
HTML Code: This is a simplified version because the real code has lots of PHP and other stuff going on.
<div id="content">
<form method="post" id="form">
Save
<ol id="navsort">
...
</ol>
</form>
</div>
As you can see the entire form, including the elements attached to Sortable, are replaced (with an identical copy of itself) via AJAX when the form is submitted. This is when Sortable stops working.
I understand that the new form was not part of the DOM when Sortable was initialized and so its not attached despite being identical to the form it replaced. I also understand how .on() is used to delegate between elements that are present when the page first loads and those added afterwards. What I do no understand is how to apply this concept to the initialization of Sortable.
I got it to work with a dirty hack, but I want to understand the right way.
Dirty Hack:
function dirtyhack(){
$("selector").on('event', 'target', function (){ ... });
}
dirtyhack();
$("#content").on('click', '#submit_menu', function (event){
event.preventDefault();
$.ajax({
type: "POST",
url: "menu-ajax.php",
data: { data:$('#form').serialize() }
}).done(function (response) {
$('#content').hide().html(response).fadeIn('slow');
dirtyhack();
});
});
So Sortable is initialized when the page loads and reinitialized when AJAX is done replacing the elements Sortable is attached to. Maybe this is right, but it feels wrong.
Disclosure: I am actually using the nestedSortable plugin in place of Sortable, but the plugin author claims that "All jQuery Sortable options, events and methods are available" through his plugin. I also did switch to the standard Sortable Widget to test and had the same issue, so I do not think its the plugin.
Plugin URL: https://github.com/mjsarfatti/nestedSortable/tree/2.0alpha

creating a record with Ember.js & Ember-data & Rails and handling list of records

I'm building an app which has layout like below.
I want to create a new post so I pressed 'new post button' and it took me to 'posts/new' route.
My PostsNewRoute is like below (I followed the method described here)
App.PostsNewRoute = Ember.Route.extend({
model: function() {
// create a separate transaction,
var transaction = this.get('store').transaction();
// create a record using that transaction
var post = transaction.createRecord(App.Post, {
title: 'default placeholder title',
body: 'default placeholder body'
});
return post;
}
});
It immediately create a new record, updates the list of the posts, and displays forms for new post.
(source: sunshineunderground.kr)
Now I have two problems.
One is the order of post list.
I expected new post will be on top of the list, but It's on the bottom of the list.
I'm using rails as my backend and I set the Post model order to
default_scope order('created_at DESC')
so old Post sits below within existing posts. but newly created one is not. (which isn't commited to backend yet)
Another is When I click created post in the list
I can click my newly created post in the posts list. and It took me to a post page with URL
/posts/null
and It's very weird behavior that I must prevent.
I think there will be two solutions.
When I click 'new post button' create a record and commit to server immediately and when server successfully saved my new record then refresh the posts list and enter the edit mode of newly created post.
or initially set Route's model to null and create a record when I click 'submit' button in a PostsNewView.
or show show only posts whose attribute is
'isNew' = false, 'isDirty' = false,
in the list..
But sadly, I don't know where to start either way...
for solution 1, I totally get lost.
for solution 2, I don't know how to bind datas in input forms with not yet existing model.
for solution 3, I totally get lost.
Please help me! which will be ember's intended way?
(I heared that Ember is intended to use same solution for every developer)
Update
now I'm using solution 3 and still having ordering issue. Here is my Posts template code.
<div class="tools">
{{#linkTo posts.new }}new post button{{/linkTo}}
</div>
<ul class="post-list">
{{#each post in filteredContent}}
<li>
{{#linkTo post post }}
<h3>{{ post.title }}</h3>
<p class="date">2013/01/13</p>
<div class="arrow"></div>
{{/linkTo}}
</li>
{{/each}}
</ul>
{{outlet}}
Update
I solved this problem by filtering 'arrangedContent' ,not 'content'
App.PostsController = Ember.ArrayController.extend({
sortProperties: ['id'],
sortAscending: false,
filteredContent: (function() {
var content = this.get('arrangedContent');
return content.filter(function(item, index) {
return !(item.get('isDirty'));
});
}).property('content.#each')
});
We use a variation of solution 3 in several places on our app. IMHO it's the cleanest of the 3 as you don't have to worry about setup/teardown on the server side This is how we implement it:
App.PostsController = Ember.ArrayController.extend({
sortProperties: ['id'],
sortAscending: true,
filteredContent: (function() {
return this.get('content').filter(function(item, index) {
return !(item.get('isDirty'));
});
}).property('content.#each')
});
Then in your Posts template you loop through controller.filteredContent instead of controller.content.
For solution 1, there are many possibilities. You could define the following event:
createPost: function() {
var post,
_this = this;
post = App.Post.createRecord({});
post.one('didCreate', function() {
return Ember.run.next(_this, function() {
return this.transitionTo("posts.edit", post);
});
});
return post.get("store").commit();
}
It creates the post then sets up a promise that will be executed once "didCreate" fires on the post. This promise transitions to the post's route only after it has come back from the server, so it will have the correct ID.
Indeed, very nice write up. Thx for that.
Doesn't your filteredContent have to use the isNew state i.o. isDirty, otherwise the Post that is being edited will not be visible.
In either case, the filteredContent property does not work in my case. I also noticed that, since I use an image as part of every element, all images will be refreshed when filteredContent is changed. This means that I see a request for every image.
I use a slightly different approach. I loop through the content and decide whether or not to display the Post in the template:
# posts.handlebars
<ul class='posts'>
{{#each controller}}
{{#unless isNew}}
<li>
<h3>{{#linkTo post this}}{{title}}{{/linkTo}}</h3>
<img {{bindAttr src="imageUrl"}}/>
<a {{action deletePost}} class="delete-post tiny button">Delete</a>
</li>
{{/unless}}
{{/each}}
</ul>
This will only show the Post object after it is saved. The url in the H3-tag also contain the id of the newly created object i.o. posts/null.
One other thing I noticed in your question: instead of passing default values to createRecord, you can use the defaultValues property on the model itself:
So, instead of:
# App.PostsNewRoute
var post = transaction.createRecord(App.Post, {
title: 'default placeholder title',
body: 'default placeholder body'
});
you can do this:
# App.Post
App.Post = DS.Model.extend({
title: DS.attr('string', {
defaultValue: "default placeholder title"
}),
body: DS.attr('string', {
defaultValue: "default placeholder body"
})
});
# App.PostsNewRoute
var post = transaction.createRecord(App.Post);
I actually wrote a function to reverse the content array a while back:
http://andymatthews.net/read/2012/03/20/Reversing-the-output-of-an-Ember.js-content-array
That's a pretty old article, almost a year old, so it probably won't work as is, but the framework is there...
You can see it in action in this mini-app I wrote:
http://andymatthews.net/code/emberTweets/
Search for users in the input field at the top and watch the left side as they appear in order from newest to oldest (instead of oldest to newest).

jQuery UI dialog form, not sending correct variable

I'm loading a customer info page using jQuery. There's a list of customers with a link next to it:
View
That triggers this function:
function load_customer(id) {
$("#dashboard").load('get_info/' + id);
}
That works perfectly. On the page I'm loading, I have a jQuery UI modal dialog form for adding new information.
<div id="addinfo">
<form><input type="hidden" name="customer_id" value="<?php echo $c->id; ?>" /></form>
</div>
My javascript:
$("#addinfobutton").click(function(){
$("#addinfo").dialog("open");
return false;
});
$("#addinfo").dialog({
autoOpen:false,
width:400,
height:550,
modal: true
});
When you select a customer the first time, it populates the hidden field correctly, but then it stays the same even after selecting other customers.
I thought that by loading a new customer page, the form would reset as well... but apparently it's being stored/cached somewhere. If I echo the ID anywhere else in the page, it shows correctly... just not in the "addinfo" div.
Any help/suggestions would be appreciated! Thanks!
JQuery dialog's dont reload the content for you when you open them. I tend to have an AJAX call replacing the content of the div that the dialog is on (or tweakng some values in it) when the dialog is opened.
If you want a hidden field, then I wouldn't put it within the dialog, you should be able to retrieve the value from outside the dialog.
After some more researching, it seems the dialog is being cached client-side after it's called. So to get around that, I just added the customerId to the end of the popup div's ID name... so each customer page will have a unique dialog ID.
However, if it's caching each of those dialogs, won't there be a performance loss if you open quite a few? How can you clear them without having to do a full page refresh?
I guess if the #addinfo div is updated it should capture the new content...anyway this would destroy the dialog after closing it to insure a new instance will be created:
$("#addinfobutton").click(function(){
openDialog('#addinfo');
return false;
});
function openDialog(elm) {
$(elm).dialog({
autoOpen:true,
width:400,
height:550,
modal: true,
close: function() {
$(this).dialog('destroy');
}
});
}

Resources