I'm still rather new to Appcelerator and using JSON files with JavaScript. I know I am parsing in the JSON file correctly, and breaking the data apart correctly, but for some reason the app can't seem to properly fetch the JSON data, and I invariably always get the onerror firing instead of the onload. Here is my code:
// Set the background color with no windows or tabs
Titanium.UI.setBackgroundColor('#000');
// Create the window
var win1 = Titanium.UI.createWindow({
title:'Challenge Window',
backgroundColor:'#fff',
});
// Store the image and its properties
var image = Titanium.UI.createImageView({
image: "https://myavantiservices.files.wordpress.com/2015/02/helloworld.gif",
height: 380,
width: 380,
center: 512,
top: -50
});
var table = Ti.UI.createTableView();
var tableData = [];
var json, line1, city, state, zip, appfile_id, id, body;
// Parse our JSON file using onload
var url = "https://raw.githubusercontent.com/JordanAshton/CodingChallenge/master/JSONtwitterOutput.txt";
var xhr = Ti.Network.createHTTPClient({
onload: function() {
json = JSON.parse(this.responseText);
for (var i = 0; i < json.things.length; i++){
var row = Ti.UI.createTableViewRow({
className: 'row',
objectName: 'row',
rowID: i,
height: 100,
borderColor: accentColor,
borderWidth: 1,
borderRadius: 5,
backgroundImage:'../images/opbg.png',
filter:json.data[i].line1 + "\n" + json.data[i].city + "," + json.data[i].state + " " + json.data[i].zip,
appfile_id: json.data[i].appfile_id,
message_id: json.data[i].id,
messagebody: json.data[i].body
});
tableData.push(row);
}
table.setData(tableData);
},
onerror: function(e) {
Ti.API.debug("STATUS: " + this.status);
Ti.API.debug("TEXT: " + this.responseText);
Ti.API.debug("ERROR: " + e.error);
alert('There was an error retrieving the remote data. Try again.');
},
timeout:5000
});
xhr.open("GET", url);
xhr.send();
// Add the image to the window and open the window
win1.add(image);
win1.add(table);
win1.open();
The JSON file I am parsing:
{"results":[
{"text":"#twitterapi https://code.google.com/archive/p/twitter-api/issues/353",
"to_user_id":396524,
"to_user":"TwitterAPI",
"from_user":"jkoum",
"metadata":
{
"result_type":"popular",
"recent_retweets": 109
},
"id":1478555574,
"from_user_id":1833773,
"iso_language_code":"nl",
"source":"twitter< /a>",
"profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg",
"created_at":"Wed, 08 Apr 2009 19:22:10 +0000"},
... truncated ...],
"since_id":0,
"max_id":1480307926,
"refresh_url":"?since_id=1480307926&q=%40twitterapi",
"results_per_page":15,
"next_page":"?page=2&max_id=1480307926&q=%40twitterapi",
"completed_in":0.031704,
"page":1,
"query":"%40twitterapi"}
}
The root node of the JSON file you are parsing is "results"; but you are iterating up to json.things.length, which at that point should be 0, then no row is created and added to the tableView.
Also those json.data[i].<some_property> look like out of context.
Hth.
Related
Hello to everyone here!
First, I searched for duplicate posts but I've not found something like my issue.
I'm making some tests (you will see some repeat in my code) to upload multiple files with bootstrap fileinput and I have some issues using the "delete" button on thumbnail previews.
The upload function is working fine, showing already uploaded images if exists with initialPreview option.
I'm trying to dynamically create the intialPreviewConfig option, and for the moment I don't know why it's not working (clicking the "delete" button give no actions at all, it's like the option is not present). However, when I manually add the intialPreviewConfig (not in a variable), it's magically working fine.
Sample javascript code (some of values are different in my true code) :
var e = 'http://localhost/foo/';
// Where other_prev is an array of img
// Where other_images_path is an array of paths to images
var other_prev = ['...'];
var other_images_path = ['...'];
var uploadOtherFileInput = function ($input) {
console.log(other_prev); // Okay : array with img tags
console.log(other_images_path); // Okay : array with urls to images
var id_to_upload;
// If id already exists
if($('#id').length) {
id_to_upload = $('#id').val();
} else {
id_to_upload = $('#id_to_upload').val();
}
// Construct initial preview config for deleting images
var initialPreviewConfig = [];
if(Array.isArray(other_images_path)) {
var xhr;
// Convert paths of images to blob objects
for(var i = 0; i < other_images_path.length; i++) {
(function (i) {
xhr = new XMLHttpRequest();
xhr.open('GET', other_images_path[i], true);
xhr.responseType = 'blob';
xhr.onload = function (ev) {
if (this.status == 200) {
var blobFile = this.response;
var splitted_path = other_images_path[i].split('/');
var img_name = splitted_path[splitted_path.length - 1];
blobFile.name = img_name;
initialPreviewConfig.push({
"caption": img_name,
"size": blobFile.size,
"url": e + 'ajax/delete_image',
"key": i + 1
});
}
};
xhr.send();
}).call(this, i);
}
}
// Console log is working
console.log(initialPreviewConfig);
// Making the bootstrap fileinput
// Working fine to show previews and upload new images
// Not working for initialPreviewConfig to delete images
$input.fileinput({
language: 'fr',
allowedFileExtensions: ["jpg", "jpeg", "png"],
uploadUrl: e + 'ajax/upload_image_sup',
uploadAsync: false, // Permet d'obtenir toutes les images en une fois et d'attendre la fin
minFileCount: 0,
maxFileCount: 20,
resizeImage: true,
showClose: false,
showCaption: false,
showBrowse: true,
showUpload: false,
showUploadedThumbs: false,
showPreview: true,
uploadExtraData: function () {
var out = {};
out.id = id_to_upload;
return out;
},
layoutTemplates: {
footer: '<div class="file-thumbnail-footer">\n' +
' <div class="file-caption-name" style="width:{width}">{caption}</div>\n' +
' {actions}\n' +
'</div>',
actions: '<div class="file-actions">\n' +
' <div class="file-footer-buttons">\n' +
' {delete} {zoom}' +
' </div>\n' +
' {drag}\n' +
' <div class="file-upload-indicator" title="{indicatorTitle}">{indicator}</div>\n' +
' <div class="clearfix"></div>\n' +
'</div>'
},
initialPreviewFileType: 'image',
initialPreview: other_prev,
initialPreviewConfig: initialPreviewConfig
});
// Okay it's a repeat thing but it's just for test purposes
if(Array.isArray(other_images_path)) {
var xhr;
for(var i = 0; i < other_images_path.length; i++) {
(function (i) {
xhr = new XMLHttpRequest();
xhr.open('GET', other_images_path[i], true);
xhr.responseType = 'blob';
xhr.onload = function (e) {
if (this.status == 200) {
var blobFile = this.response;
var splitted_path = other_images_path[i].split('/');
var img_name = splitted_path[splitted_path.length - 1];
blobFile.name = img_name;
// Add preview to stack for overwrite
$input.fileinput('addToStack', blobFile);
}
};
xhr.send();
}).call(this, i);
}
}
};
Console logs of other_prev and other_images_path are okay to create the previews.
Now the console log of the variable initialPreviewConfig is (width is not present but I removed it, no changes) :
[]
0: {caption: "xxx.jpeg", size: 381939, url: e + "ajax/delete_image", key: 1}
1: {caption: "yyy.jpeg", size: 381939, url: e + "ajax/delete_image", key: 2}
2: {caption: "zzz.jpeg", size: 381939, url: e + "ajax/delete_image", key: 3}
3: {caption: "aaa.jpeg", size: 381939, url: e + "ajax/delete_image", key: 4}
4: {caption: "bbb.JPG", size: 2442700, url: e + "ajax/delete_image", key: 5}
length: 5
__proto__: Array(0)
So everything seems to be OK... But that is not working for deleting.
However, if I manually create the config it's working "fine" (the ajax/delete_image route is not created at the moment but the click is OK and I get an error for this).
Example (values are not important here) :
...
initialPreviewFileType: 'image',
initialPreview: other_prev,
initialPreviewConfig: [
{caption: "xxx.jpg", size: 576237, width: "120px", url: e + "ajax/delete_image", key: 1},
{caption: "yyy.jpg", size: 576237, width: "120px", url: e + "ajax/delete_image", key: 2},
...
]
...
Can somebody help me with this issue or facing / faced the same ?
Tell me if you want more examples or a JSFiddle.
For this test, I'm using :
PHP 5.6 with framework CodeIgniter 3
Chrome 75
Bootstrap 3.3.7
jQuery 3.1.1
Bootstrap Fileinput 4.4.3
I also could use jQuery ($.ajax, $.get...) instead of XMLHttpRequest but it's not the question I think.
Thanks!
Good day!
Okay so it was as always an async problem.
It could be solved in many ways like promises or callback functions, but to keep consistent with the code I'm now creating the array of objects from the server before loading the page (like initialPreview thumbnails).
Sorry for the inconvenience!
I am currently working on fetching/scraping all the images being received on requesting a URL.
The problem i am facing is the response changes after few tries or is very inconsistent even for the same URL using the phantomjs.
I have tried clearing cache multiple times and at different location in my code but the the request numbers dont appear to be the same.
o/p
1st call to URL
19 images and total off 49 responses from server
2nd call
14 images and 48 responses from server
3rd call
14 images and 38 responses from server
Output for execution twice
phantomjs-2.1.1-windows
running a java-script code using the phantomjs command
I am very new to Javascript, i have manged to write the following till now
var page = require('webpage').create();
var fs = require('fs');
var url = "https://..........";
page.settings.clearMemoryCaches = true;
page.clearMemoryCache();
page.clearCookies();
page.viewportSize = {width: 1280, height: 1024};
var imageCounter = 0;
var responseCounter = 0;
page.open(url, function (status) {
console.log(status + '*/*/*/*/*/*/**/*/*/*/*/*/*/*/*/*/*/*/*/*/')
if(status=='success'){
console.log('The entire page is loaded.............################');
console.log('\n' + imageCounter + '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n');
console.log('\n' + responseCounter + '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n');
imageCounter = 0;
page.clearMemoryCache();
page.clearCookies();
page.close();
phantom.exit();
}
});
page.onResourceReceived = function(response) {
if(response.stage == "start"){
responseCounter++;
var respType = response.contentType;
if(respType.indexOf("image")==0){
imageCounter++;
//console.log('Content-Type : ' + response.contentType)
//console.log('Status : ' + response.status)
//console.log('Image Size in byte : ' + response.bodySize)
//console.log('Image Url : ' + response.url)
//console.log('\n');
console.log(imageCounter + '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n');
}
}
};
I want to get consistent response for the images at least, I am really confused how does phantom cache this kind of resources on second attempt.
I have some data in a Google Spreadsheet, which I'm pulling with Tabletop.js. I'm able to display my data as well in my showInfo() function. All good.
Now I try to achieve to display this data in a default Google Chart. In this case I use their map package to display my data on a map. They provided me some sample code, see here.
At the moment I'm struggling with the following function, whole code in my Fiddle:
mapdata.addRows([
// PREFERRED DATA TO COLLECT:
// [data.geoloc, data.GM_NAAM, 'blue' ],
// THE HARDCODED WAY:
['Kerkbrink 2 ANLOO', 'ANLOO', 'green'],
['Grote Kerkstraat 32 WIJK EN AALBURG', 'WIJK EN AALBURG', 'blue']
]);
How do I approach? I managed to display all data with a forEach in the showInfo function, but this does not work in this array.
I'm pretty new in JS-land, so I appreciate every help on achieving this.
Thanks in advance!
please find the updated fiddle here
is this what you are looking for ?
updated js code:
/* 1: tabletop shizzle */
var publicSpreadsheetUrl = 'https://docs.google.com/spreadsheets/d/1cpSdKrQK0DfRoC9_xgmAqwVaDqw8BKMOL7drA_QFIn0/edit?usp=sharing';
function init() {
Tabletop.init( { key: publicSpreadsheetUrl,
callback: showInfo,
simpleSheet: true } )
}
function showInfo(data, tabletop) {
alert('Successfully processed!')
var chartdiv = document.querySelector(".chart_div");
console.log("data: ", data);
data.forEach( function(data) {
// card
var card = document.createElement('div');
card.classList.add("card");
//content
var content = document.createElement('div');
content.classList.add('content');
content.innerHTML = data.GM_NAAM + ' ' + data.geoloc;
// append
chartdiv.appendChild(card);
card.appendChild(content);
});
// trigger the google charts drapMap function and send the data to it
drawMap(data);
}
window.addEventListener('DOMContentLoaded', init);
/* 2: google chart shizzle */
google.charts.load('current', {
'packages': ['map'],
// DEFAULT GOOGLE API KEY
'mapsApiKey': 'AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY'
});
google.charts.setOnLoadCallback(drawMap);
function drawMap(data) {
var mapdata = new google.visualization.DataTable();
mapdata.addColumn('string', 'Address');
mapdata.addColumn('string', 'Location');
mapdata.addColumn('string', 'Marker')
console.log(data);
data = data ? data : [];
var rows = [];
data.forEach(function(item){
rows.push([item.GM_NAAM, item.geoloc, 'green'])
})
// THIS IS THE PART I NEED THE DATA.
mapdata.addRows(rows);
// SOME OPTIONS
var url = 'https://icons.iconarchive.com/icons/icons-land/vista-map-markers/48/';
var options = {
zoomLevel: 7,
showTooltip: true,
showInfoWindow: true,
useMapTypeControl: true,
icons: {
blue: {
normal: url + 'Map-Marker-Ball-Azure-icon.png',
selected: url + 'Map-Marker-Ball-Right-Azure-icon.png'
},
green: {
normal: url + 'Map-Marker-Push-Pin-1-Chartreuse-icon.png',
selected: url + 'Map-Marker-Push-Pin-1-Right-Chartreuse-icon.png'
}
}
};
var map = new google.visualization.Map(document.getElementById('map_div'));
map.draw(mapdata, options);
}
I want show some option of picker just when user log in.
So I tried====>
I added a global variable in alloy.js>>
var aux=0;
And in my index.js i did>>
loginReq.onload = function()
{
var json = this.responseText;
var response = JSON.parse(json);
if (response.logged == true)
{
aux=1;
homes.open();
$.index.close();
alert("Welcome " + response.name + ". Your email is: " + response.email);
}
else
{
alert(response.message);
}
};
Last part in other controller home.js>>
Alloy.Globals.loading.hide();
var picker = Ti.UI.createPicker({
left:10,
top: 10,
height:50,
opacity:0.5, backgroundColor: "Black", borderRadius:"2"
});
var data = [];
data.push(Titanium.UI.createPickerRow({id:'0',title:'Mis cursos'}));
data.push(Titanium.UI.createPickerRow({id:'1',title:'Todos Cursos'}));
if(aux===1){
data.push(Titanium.UI.createPickerRow({id:'2',title:'Crear'}));
}
picker.add(data);
the problem is I tried to use if(aux===1) or if(aux==1), but didnt work both... Always no show "Crear" in picker. Why? Need your helps! and Thanks!
The issue is that you are not defining your global variable in alloy.js properly.
As you defined var aux=0; in alloy.js , so this variable is only available in alloy.js. To make it usable in other files define it as following :
In alloy.js :
Alloy.Globals.aux = 0;
To fetch it in index.js and home.js use :
var auxValue = Alloy.Globals.aux;
So your home.js would look like :
Alloy.Globals.loading.hide();
var picker = Ti.UI.createPicker({
left:10,
top: 10,
height:50,
opacity:0.5, backgroundColor: "Black", borderRadius:"2"
});
var data = [];
data.push(Titanium.UI.createPickerRow({id:'0',title:'Mis cursos'}));
data.push(Titanium.UI.createPickerRow({id:'1',title:'Todos Cursos'}));
if(Alloy.Globals.aux === 1){ //changed here
data.push(Titanium.UI.createPickerRow({id:'2',title:'Crear'}));
}
picker.add(data);
Note : also check the proper way to manage objects in alloy.js
My mobile titanium app is loading data from a remote url, in the form of a xml file, that I have generated. The idea is that the app is downloading that xml and processes it into a SQLlite db. But I was thinking, I could also replace the server side generated xml with a ready sqlite file with all the data in it. That way, I don't need to do any processing client side, which saves some of the user's time.
Is this a good idea? If yes, how would I "fake" generate a sqlite file? There are no headers that I need to pass on, so that's not the issue, but what I noticed when I opened an .sqlite file with coda, that there are weird characters meaning the encoding must be different.
Thanks!
There is no mechanism in any browsers I am aware of which would allow you to transfer a binary image of a sqlite database (even assuming byte ordering and character coding were not an issue).
Certainly, it may improve performance to maintain / transfer the data in a format closer to a set of CREATE and INSERT statements than atomic XML.
You can create a SQLite database in many programs - I use MesaSQLLite. It is just a binary file. Here is an example of downloading and installing on iOS. I have not tested it on Android.
var win = Ti.UI.createWindow({
backgroundColor: '#eee'
});
var button1 = Ti.UI.createButton({
width: '60%',
height: 30,
top: 5,
title: '1) Get Database'
});
var button2 = Ti.UI.createButton({
width: '60%',
height: 30,
top: 40,
title: '2) Install Database'
});
var button3 = Ti.UI.createButton({
width: '60%',
height: 30,
top: 75,
title: '3) Query Database'
});
var msg = Ti.UI.createLabel({
top: 110,
width: '80%',
height: 120,
text: 'click the buttons in order',
font: {
fontFamily: 'Courier',
fontSize: 12
},
textAlign: 'center'
});
win.add(button1);
win.add(button2);
win.add(button3);
win.add(msg);
function localFile() {
var localDbFile = 'testdb.db';
return Ti.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory, localDbFile);
}
button1.addEventListener('click',
function() {
msg.text = "Getting Database from Server";
var xhr = Ti.Network.createHTTPClient();
xhr.open("GET", "http://www.titaniumdevelopment.com.au/testdb.db");
xhr.onload = function() {
if (xhr.status == 200) {
msg.text = "saving database";
var file = localFile();
file.write(this.responseData);
msg.text = "database saved as " + file.nativePath;
} else {
msg.text = "Unable to get database. Response code was " + xhr.status;
}
};
xhr.onerror = function() {
msg.text = "Error fetching database";
};
xhr.send();
});
button2.addEventListener('click',
function() {
var file = localFile();
if (file.exists()) {
msg.text = "installing database";
Ti.Database.install(file.nativePath, 'test');
msg.text = "database installed";
} else {
msg.text = "unable to find database";
}
});
button3.addEventListener('click',
function() {
try {
var db = Titanium.Database.open('test');
var rows = db.execute('SELECT * FROM TIPS');
msg.text = 'ROW COUNT = ' + rows.getRowCount();
rows.close();
db.close();
} catch(e){
msg.text = "unable to open database";
}
});
win.open();
Note: I'm not sure the App Store would approve this as they are not too keen on fetching data remotely.