How to populate a jQuery Mobile ListView with JSON data? - json

I'm developing a webapp here using HTML and jQuery Mobile (JQM), so I'm pretty new at this.
What I'm trying to do here is to populate a JQM listview with a list of names.
Each of this names will link to a new page with personal data being displayed (Full name, address, date of birth, etc).
Currently, because of my lack of knowledge, I manually create a new .html file for EACH individual person (e.g. johnDoe.html for a fictional character Mr. John Doe). I then physically link the list elements to this html file via the function.
Problem is now I have 100 over individuals to populate that list view. I think that there's an easier way to do this rather than manually creating 100+ html files for all these individual persons right?
I heard of this JSON thing that might do the trick, but coming from a background of ZERO computing knowledge, I don't really understand how it works. Will someone please shed some light on how can I do this?
Thanks a lot!
EDIT:
I'm using Dreamweaver CS5.5 to do the coding. For this webapp that I'm tasked to develop, I was given a "template" or sorts that uses JQM and Backbone.js. As such, somehow the "multi-page" structure for a single HTML file doesn't seem to work. From what I see in the template, every HTML file has a corresponding JS file that has code that looks like this:
define(['jquery',
'underscore',
'backbone',
'helper',
'views/phr/list',
'text!templates/vpr2.html'
],
function ($,
_,
Backbone,
Helper,
phrListView,
tmpVpr2) {
var view = Backbone.View.extend({
transition: 'fade',
reverse: true,
initialize: function () {
this.phrlistview = new phrListView();
},
render: function () {
$(this.el).append(_.template(tmpVpr2, {}));
console.log("Rendering subelements...");
Helper.assign(this, {
'#phrListView': this.phrlistview
});
return this.el;
}
});
return view;
});
For the HTML pages, they all begin with a <div data-role=header> tag, then a <div data-role=content>, before ending with a <div data-role=footer>, all with their respective content within the opening and closing tags.
For my listview in question, the JQM code for the listview will be within the <div data-role=content> part of the HTML file. How can I populate this listview with JSON data then?
(Apologies if I sound extremely noob at this, because I really am >.< Really appreciate the help!)

Solution
Yes. Its possible to have two pages and use one for displaying your data and one to show up the details of the clicked item. I had to pull in some old stuff, a demo I made when jQM was in version 1.1 and change it to modern times. Anyway, considering I have an array like this :
[
{
"id": 0,
"age": 31,
"name": "Avis Greene",
"gender": "female",
"company": "Handshake",
"email": "avisgreene#handshake.com",
"phone": "+1 (845) 575-2978",
"address": "518 Forrest Street, Washington, New York, 3579"
},
{
"id": 1,
"age": 31,
"name": "Dunn Haynes",
"gender": "male",
"company": "Signity",
"email": "dunnhaynes#signity.com",
"phone": "+1 (829) 454-3806",
"address": "293 Dean Street, Dante, Oregon, 5864"
}
]
I randomly generated stuff and made it upto 100 elements, just like how you seem to have. I have two pages.
<!--first page -->
<div data-role="page" id="info-page">
<div data-role="header" data-theme="b">
<h1> Information</h1>
</div>
<div data-role="content">
<ul data-role="listview" id="prof-list" data-divider-theme="a" data-inset="true">
<li data-role="list-divider" data-theme="b" role="heading">Names</li>
</ul>
</div>
</div>
<!--second page -->
<div data-role="page" id="details-page">
<div data-role="header" data-theme="b">Go back
<h1>Employee Details</h1>
</div>
<div data-role="content"></div>
</div>
The first page, #info-page is for showing data in a listview. The second page, #details-page is for the info of the clicked item. Thats all you need. Only two pages, not more than that. So every time a click happens, you do the following through JavaScript
Get the current value of data from the array. Like if you click on the 4th li in the list, get the 4th object from the array which has all the data.
Store it in the data variable of the second page, so that it can be retrieved later. Something like this:
$("#details-page").data("info", info[this.id]);
Then, redirect to second page using changePage, like this :
$.mobile.changePage("#details-page");
When the second page opens, use the pagebeforeshow event to get the data from the page (which you stored into this page when the tag in the previous page was clicked.
Use some HTML layout to populate the data. I used jQM's grids.
That's all folks!
Full code
Ive attached the JS used with the HTML. Its self explanatory. Read the inline comments in the code and you'll be able to understand more. Assume info is the array in picture.
//pageinit event for first page
//triggers only once
//write all your on-load functions and event handlers pertaining to page1
$(document).on("pageinit", "#info-page", function () {
//set up string for adding <li/>
var li = "";
//container for $li to be added
$.each(info, function (i, name) {
//add the <li> to "li" variable
//note the use of += in the variable
//meaning I'm adding to the existing data. not replacing it.
//store index value in array as id of the <a> tag
li += '<li>' + name.name + '</li>';
});
//append list to ul
$("#prof-list").append(li).promise().done(function () {
//wait for append to finish - thats why you use a promise()
//done() will run after append is done
//add the click event for the redirection to happen to #details-page
$(this).on("click", ".info-go", function (e) {
e.preventDefault();
//store the information in the next page's data
$("#details-page").data("info", info[this.id]);
//change the page # to second page.
//Now the URL in the address bar will read index.html#details-page
//where #details-page is the "id" of the second page
//we're gonna redirect to that now using changePage() method
$.mobile.changePage("#details-page");
});
//refresh list to enhance its styling.
$(this).listview("refresh");
});
});
//use pagebeforeshow
//DONT USE PAGEINIT!
//the reason is you want this to happen every single time
//pageinit will happen only once
$(document).on("pagebeforeshow", "#details-page", function () {
//get from data - you put this here when the "a" wa clicked in the previous page
var info = $(this).data("info");
//string to put HTML in
var info_view = "";
//use for..in to iterate through object
for (var key in info) {
//Im using grid layout here.
//use any kind of layout you want.
//key is the key of the property in the object
//if obj = {name: 'k'}
//key = name, value = k
info_view += '<div class="ui-grid-a"><div class="ui-block-a"><div class="ui-bar field" style="font-weight : bold; text-align: left;">' + key + '</div></div><div class="ui-block-b"><div class="ui-bar value" style="width : 75%">' + info[key] + '</div></div></div>';
}
//add this to html
$(this).find("[data-role=content]").html(info_view);
});
Demo
I've also made a demo where you can read more about this at jsfiddle.net.
Here's the link : http://jsfiddle.net/hungerpain/52Haa/

you can try something like this
Updated
Html Page
<div data-role="page" id="testpage">
<div data-role="content">
<ul data-role="listview" id="listitem" data-divider-theme="a" data-inset="true">
</ul>
</div>
</div>
javascript
$(document).on("pageinit", "#testpage", function(){
$.getJSON("example.json", function(data){
var output = '';
$.each(data, function(index, value){
output += '<li>' +data+ '</li>';
});
$('#listitem').html(output);
});
});

Related

Adding reviewers image from Google Places Review

I am currently using this jQuery plugin on my site to pull in a single user review from my Google Places account:
https://github.com/peledies/google-places (unmodified).
It works ok - however I need to extend the code to somehow also pull in and display the avatar/image of the reviewer that I'm pulling in.
<script>
jQuery(document).ready(function() {
jQuery("#google-reviews").googlePlaces({
placeId: 'ChIJ_____....766FU3cfuE',
render: ['reviews'],
min_rating: 2,
max_rows: 0,
personsName: 'Jt D.'
});
});
</script>
I need to extend this to also include the avatar/image of the single review I am displaying.
I have found "profilePhotoUrl" is in the "reviewer" object in the JSON (https://developers.google.com/my-business/reference/rest/v4/accounts.locations.reviews) but I can't work out how to add that to the existing code.
After a bit of twiddling around, I found the following solution.
in the google-places.js, find the renderReviews function:
var renderReviews = function(reviews)
add this:
var img = reviews[i].profile_photo_url;
This returns the image stored in the review as a URL. You can then add it to the output within an img html tag:
if (name === plugin.settings.personsName) { html = html+" <img src="+ img +"> <div class='review-item'><div class='review-meta'><span class='review-author'>"+name+" | <span class='review-date'>"+date+"</span></div>"+stars+"<p class='review-text'>"+reviews[i].text+"</p></div>"
}

Wixcode "static" html iframe

Apologies if this is not the correct place to post this. I'm completely new to HTML and such, but I wanted to put a button on my website which would remember how many times it been pressed and each time someone presses it it give you a number, say for example the next prime number. With enough googleing I managed to put together some (what I expect is really bad code) which I thought could do this. This is what I have (sorry if its not formatted correctly, I had trouble with copy pasting).
<head>
<title>Space Clicker</title>
</head>
<body>
<script type="text/javascript">
function isPrime(_n)
{
var _isPrime=true;
var _sqrt=Math.sqrt(_n);
for(var _i=2;_i<=_sqrt;_i++)
if((_n%_i)==0) _isPrime=false;
return _isPrime;
}
function nextPrime(_s,_n)
{
while(_n>0)if(isPrime(_s++))_n--;
return --_s;
}
var clicks = 0;
function hello() {
clicks += 1;
v = nextPrime(2,clicks);
document.getElementById("clicks1").innerHTML = clicks ;
document.getElementById("v").innerHTML = v ;
};
</script>
<button type="button" onclick="hello()">Get your prime</button>
<p>How many primes have been claimed: <a id="clicks1">0</a></p>
<p>Your prime: <a id="v">0</a></p>
</body>
The problem is that when I put this code in a iframe on my wixsite it seems to reload the code each time you look at the site, so it starts the counter again. What I would like it say the button has been pressed 5 times, it will stay at 5 until the next visitor comes along and presses it. Is such a thing possible?
You don't actually need an iframe for that, You can use wixCode to do that. WixCode let's you have a DB collection. and all you need to do is update the collection values on every click.
Let's say you add an Events collection can have the fields:
id, eventName, clicksCount
add to it a single row with eventName = 'someButtonClickEvent' and clicksCount = 0
Then add the following code to your page:
import wixData from 'wix-data';
$w.onReady(function () {});
export function button1_click(event) {
wixData.get("Events", "the_event_id")
.then( (results) => {
let item = results;
let toSave = {
"_id": "the_event_id",
"clicksCount": item.clicksCount++
};
wixData.update("Events", toSave)
})
}
now you need to add button1_click as the onClick handler of your button (in the wixCode properties panel).

How do I format my AngularJS data model?

Hi I am just beginning with angular and I am struggling to find the answer to what I'm sure is quite a simple thing to do.
I am currently getting the values of some input boxes and pushing them into my scope. This is creating one long 'array' eg:
['data-1','data-2','data-3']
I would like to format my data in the following way instead
$scope.data = [
{
'header1': 'data1-1',
'header1': 'data1-2',
'header1': 'data1-3'
},
{
'header1': 'data2-1',
'header1': 'data2-2',
'header1': 'data2-3'
}
]
This is my function as it currently is.
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
$scope.td.push($(value).val());
});
}
Any help or pointers would be greatly appreciated as I am just getting my head round the angular way
Doing this isn't hard... but before I give you a gun to shoot yourself in the foot, just to say that I think it would be beneficial to explain WHY you want structure in that other format you are mentioning. You seem to have lots of data repetition and that's always a red flag.
Now for the code, you just need to create object before pushing it to the array like:
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
var obj = {
"header1": val + "-1",
"header2": val + "-2"
};
$scope.td.push(obj);
});
}
EDIT:
OK, so you are trying to add new row to the table. First of all, you shouldn't be doing angular.forEach, but rather those input elements in HTML should bind to existing scope object, like:
// obviously use better names than Input1Value
// I am here just giving you example
$scope.bindData = {
Input1Value: null,
Input2Value: null
};
// in HTML you will do
// <input ng-model="bindData.Input1Value" />
// <input ng-model="bindData.Input2Value" />
Now that you've eliminated that nasty angular.forEach you need to have some kind of event handler, for example when user clicks the button you want to add this object to the array to which table is data bound. Just be sure to clone the $scope.bindData object when you add it to array.
$scope.createRow = function(){
var newRowData = $scope.cloneObject($scope.bindData);
$scope.td.push(newRowData);
}
// http://heyjavascript.com/4-creative-ways-to-clone-objects/
// https://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
$scope.cloneObject = function(objToClone) {
var newObj = (JSON.parse(JSON.stringify(objToClone)));
}
To close this answer off - keep in mind, if you ever find yourself directly referencing HTML DOM elements in Javascript with AngularJS - you are doing something wrong. It's a nasty habit to eliminate, especially if you are coming from jQuery background (and how doesn't?), where everything is $("#OhHiThere_ElementWithThisId).
Obviously the main thread on this topic on StackOverflow is this one:
“Thinking in AngularJS” if I have a jQuery background?
However I find that it's too theoretical, so Google around and you may find better overviews like:
jQuery vs. AngularJS: A Comparison and Migration Walkthrough

dynamically get options of a dropdown menu in angular

The main problem is that I have a dropdown menu whose options should be updated dynamically.
The workflow is as follows:
I have an input element connected to an ng-model called toSubmit that when longer than 3 characters should fire an http.get call to fetch the list that should populate the dropdown menu.
So this list will change everytime the toSubmit variable changes. Let's call this list database (in the controller it is $scope.database.
What I am trying right now is a very simple solution that doesn't work most probably because the html DOM that contains the dropdown list is loaded at the very beginning and does not keep track of the changes in the options.
In my controller I have the following part which watches over toSubmit:
$scope.toSubmit = '';
$scope.$watch('toSubmit',function(query){
if (query.length >= 3){
getQueryDatabases.companyNameService({'field':'name','query':query,'numberOfHits':'10'},'CIK').prom.then(
function(dataObject){
$scope.database = dataObject;
// dataObject.forEach(function(item){
// $scope.databaseString.push(item.cik + ' ' + item.companyName);
});
});
}
});
And my html looks like the following:
<label for="nameCompany">Name:</label>
<input type="text" ng-model="toSubmit"></input>
<select ng-model="database" ng-options="line in database"></select>
Now my take was take by binding database with ng-Model I would get the result but I am most likely wrong. Can someone please help me?
I recommend you to use select2 that'll handle things like limiting input before server request and have great look and extendibility.
You need to add angular-ui-select2 to your project.
Here is code for you:
Html:
<input class='form-control' data-ng-model='position.company' data-ng-required data-placeholder='Company:' data-ui-select2='employerSelect2Options' id='company_name' type='hidden'>
JavaScript:
$scope.employerSelect2Options = {
minimumInputLength: 2,
query: function (query) {
var _query = query;
var companies = Restangular.all('companies').getList({query: query.term});
companies.then(function(data) {
var results = {results: []};
_.each(data, function(element, index, list) {
results.results.push({id: element.id, text: element.name});
})
if(!_.contains(_.map(data, function(element){ return element.name; }), _query.term)) {
results.results.push({id: _query.term , text: 'Create company "' + _query.term + '"'});
}
_query.callback(results);
})
}
};
My example also contains logic for add "create company" if zero results returned. In this case position.company will contain text of non found company name in id field and you can check it on server side and create one before assigning id.
This logic in
if(!_.contains
condition.

jqPlot charts on page load

I have a form where I select the number of items. Upon clicking submit, it should take me to a new page where it would display the item selected and depending on the number of items selected, it would create those many jqPlots, one for each item.
Any suggestions on how do I go about doing this?
Thanks,
S.
It's hard to give any specifics without more detail about the items, but basically you would pass a JSON structure to your view with the items to be plotted. Then you would loop through the JSON structure, creating DIV tag for each item to be plotted and appending the DIV tags to the body.
The Javascript part would look something like this:
$.each(items, function(index, value) {
$myPlot = $("<div>");
$myPlot.attr("id", "item"+index);
$.jqplot($myPlot.attr("id"), ...);
$("body").append($myPlot);
});
This question is very general, but answering (specifically and only) the question of loading multiple charts:
You need a unique HTML div id for each chart; consider using an RFC 4122 UUID (generate as needed) for each chart/div rather than a sequential index for each. Use something that looks like this as a placeholder div for each:
<div class="chartdiv" id="chartdiv-${UID}">
<a rel="api" type="application/json" href="${JSON_URL}" style="display:none">Data</a>
</div>
This embeds the JSON URL for each div inside it, in a hidden hyperlink that can be discovered by JavaScript iterating over your multi-chart HTML page.
The matter of the UUID is inconsequential -- it just seems the most robust way to guarantee a unique HTML id addressable by JavaScript for each chart.
Subsequently, you should have JavaScript that looks something like:
jq('document').ready(function(){
jq('.chartdiv').each(function(index) {
var div = jq(this);
var json_url = jq('a[type="application/json"]', div).attr('href');
var divid = div.attr('id');
jq.ajax({
url: json_url,
success: function(responseText) { /*callback*/
// TODO: responseText is JSON, use it, normalize it, whatever!
var chartdata = responseText;
jq.jqplot(divid, chartdata.seriesdata, chartdata.options);
}
});
});
});