Querying azure mobile services with javascript - javascript

I am working on a mobile application using apache cordova.. I have a javascript file that reads data from an azure mobile service and persists it onto the local SQLITE database..
i have a function that is meant to query the azure mobile service but it gives me an error -"cannot read propert 'take' of undefinedType". The function is as follows
function refreshQuestionsTable() {
alert("questions refreshing");
var query = QuestionsTable.take(100).read().done(function (results) {
alert(results.length); //see how many records were returned
for (var i = 0; i < results.length; i++) {
alert(results[i].question); //display the question returned
commitQuestions(results[i].question_id, results[i].client_ref, results[i].question_set, results[i].question_dept, results[i].question, results[i].question_branch, null);
}
},function (err) {
alert("Error: " + err);
});
}

It looks like your client instantiation piece is wrong.
var client = new MobileServiceClient('feedbackmaster.azure-mobile.net/', 'oLMEOExWGFolBhpyYpTFkqvKuLNlyL91');
You had a ';' at the end of your URL for some reason. You should also do a .where() clause that is universally true. Take should work off of that.

Related

Node.js update client-accessible JSON file

A beginner's question as I am new to web programming. I am using the MEAN stack and writing a JSON file within the server in order to make some weather information available to any connected clients.
I am updating the JSON file every hour using the node-schedule library. Will the constant updating of the file from the server cause any concurrency issues if the clients happen to be attempting to access the file's data at the same time?
Code snippet below:
server.js
function updateWeatherFile() {
var weather = require('weather-js');
var w = "";
weather.find({search: weatherSearch, degreeType: 'C'}, function(err, result) {
if(err)
console.log(err);
w = JSON.stringify(result, null, 2);
fs.writeFile('public/weather.json', w, function(err) {
if(err) {
console.log(err);
}
});
});
}
if(scheduleWeather) {
var schedule = require('node-schedule');
var sequence = '1 * * * *'; // cron string to specify first minute of every hour
var j = schedule.scheduleJob(sequence, function(){
updateWeatherFile();
console.log('weather is updated to public/weather.json at ' + new Date());
});
}
else {
updateWeatherFile();
}
client_sample.js
// get the current weather from the server
$http.get('weather.json').then(function(response) {
console.log(response['data'][0]['current']);
vm.weather = response['data'][0]["current"].skytext;
vm.temperature = response['data'][0]["current"].temperature;
});
NodeJs is single threaded environment.
However To read and write files Node starts external processes and eventually the file can be accessed to read and write simultaneously. In this case the concurrency is not handled by Node, but by the Operational System.
If you think this concurrency may harm you program, consider using a lock file as commented and explained here.

Saved (globally defined) array is emptied after callback

I am using the spotify-web-api-js node module, JavaScript, and jQuery to log into a user's Spotify account and save an array of tracks to a newly created playlist, but so far I can only get a blank playlist created.
I gather all of the tracks through the API and put them in a globally defined array as I show the list to the user. This array is defined on line 39: var song_uris = [];
console.log shows that this array is filled with the URIs I need after this function, which occurs upon initial search:
function searchArtists(originalArtist, callback) {
$(window).on('hashchange', function() {
console.log(window.location.hash);
});
console.log('originalArtist', originalArtist);
$.getJSON("https://api.spotify.com/v1/search?type=artist&q=" + originalArtist, function(json) {
$('#artist').html('<p>'+ '<img src="' + json.artists.items[0].images[2].url + '" height="100" width="100" /> ' + json.artists.items[0].name +'</p>');
var originalArtistId = json.artists.items[0].id;
s.getArtistRelatedArtists(originalArtistId, function(err, data) {
relatedArtists = {};
for (var i = 0; i < data.artists.length; i++) {
relatedArtists[data.artists[i].id] = {};
relatedArtists[data.artists[i].id].name = data.artists[i].name;
relatedArtists[data.artists[i].id].id = data.artists[i].id;
}
var counter = 0;
for (var id in relatedArtists) {
relatedArtists[counter] = relatedArtists[id];
delete relatedArtists[id];
counter++;
}
async.times(counter, function(n, next) {
console.log(n);
console.log(relatedArtists[n].id);
s.getArtistTopTracks(relatedArtists[n].id, "US", function (err, data2) {
relatedArtists[n].song = data2.tracks[0].name; //sometimes this is a TypeError? idk
relatedArtists[n].uri = data2.tracks[0].uri;
$('#related-artist').append('<p><strong>' + relatedArtists[n].name + '</strong> -- \"' + relatedArtists[n].song + '\"</p>');
song_uris.push(relatedArtists[n].uri);
next(null, relatedArtists[n].uri);
});
}, function(err, song_uris) {
console.log(song_uris); //array is full here
});
});
});
}
However, as soon as people go through the optional login/callback process, I get all of the user's data (including the new playlist URI), but the song_uris array I had before is now empty.
if (params.access_token) {
s.setAccessToken(params.access_token);
s.getMe().then(function(data) {
// and here it goes the user's data!!!
console.log(data);
console.log(data.id);
user_id = data.id;
s.createPlaylist(user_id, {name: 'Related Artist Playlist'}).then(function(data3) {
console.log(data3);
playlist_id = data3.uri;
playlist_id = playlist_id.substring(33);
console.log(playlist_id);
console.log(user_id);
console.log(song_uris); //array is empty here
s.addTracksToPlaylist(user_id, playlist_id, song_uris).then(function(data){
console.log(data);
});
});
});
}
Since this is one of the parameters of the addTracksToPlaylist function, the XML request doesn't work.
Here is a screenshot of my console, where I see a failed POST request and an uncaught exception: object XMLHttpRequest:
There is obviously an issue with the scope and/or async, but I can't seem to figure out where to put addTracksToPlaylist instead. Putting it immediately after the line where console.log outputs a full array either doesn't do anything (if it's within a window.onhashchange = function ()), or gives an error because the user and playlist are undefined before the callback (if the addTracksToPlaylist function is there alone).
Is the data being overwritten when the site refreshes post-callback? If so, how can I stop that so I have a full array of URIs that fit in the URL string? (According to the Spotify Developer docs, the URL should have the comma-separated list of track URIs (which was in my array) passed to it through the uri parameter, but because of the empty array it breaks and I get https://api.spotify.com/v1/users/account/playlists/5kxeeKym1tpEx8Trj3qkd5/tracks?uris= (with the empty parameter). How can I solve this? Or is there an alternate way to keep the desired user flow of Search for Artist -> See List of Songs from Related Artists -> (Optional) Login To Spotify Account -> (Optional) Save List of Songs from Related Artists to New Playlist?
I've created a jsfiddle with the full JS and HTML code as well: https://jsfiddle.net/37Lkrcb1/1/
Notes: This is running on a Node.js server and I use bundle and browserify to compile the script.
It sounds like at least one path in your app will involve a page refresh you can't avoid (going to Spotify and coming back). In that case, all your variables will be reset by the page load.
If you have song_uris you want to persist across that reload, you can store them in sessionStorage (or localStorage) prior to the Spotify authentication, and retrieve them when the page loads.
Storing it:
sessionStorage.setItem("song_uris", JSON.stringify(song_uris));
// Or localStorage.setItem...
Retrieving it on page load:
var song_uris = JSON.parse(sessionStorage.getItem("song_uris") || "null");
if (!song_uris) {
// There weren't any in storage, populate in another way or set dfeault
}
Web storage is nearly universally supported.

'alert' is undefined JavaScript runtime error

Whenever I launch my Windows Store application, I get an error that 'alert' is undefined. I'm trying to query a table in Azure Mobile Services and make a list out of the column "type_of_service" from that table.
var typeOfServiceTable = client.getTable('TypeOfService');
// Create a list of type of services
var query = typeOfServiceTable.select("type_of_service").read().done(function (results) {
alert(JSON.stringify(results));
}, function (err) {
alert("Error: " + err);
})
alert() can not be used on Windows Store application.
Instead of using alert(), try the following function:
var msg = new Windows.UI.Popups.MessageDialog("Hello Windows!");
msg.showAsync();

What problems would I run into if I make all Data available to all Controllers?

My AngularJS CRUD application processes it's information over a WebSocket Server. (This was mainly so that updates from one user would get automatically pushed to all users without the need for massive HTTP polling)
I realized early on that I would have to set up my services differently than I normally do with HTTP services. Normally, for each Model that I am working with, I give them their own service to populate that particular Model. However, this is not feasible with a Websocket Connection, because I don't want a separate connection for each service. Therefore, there are a couple of solutions.
1) set up a single service that establishes a connection, then share that connection with other services that will use that service to make their specific queries
2) make a single, type-agnostic service that will be used by all controllers that need access to the connection and data.
Option 2 seemed much easier to manage and would be reusable across applications, so I started on that. That was when I realized that this was actually an opportunity. Rather than explicitly creating models for each type of data that the Client could receive, I could create a master data object, and dynamically create child objects of myService.data as needed when data flows in from requests. Thus, if I ever need to update my Model, I just update the Model at the server level, and the client already knows how to receive it; it will just need a Controller that knows how to use it.
However, this opportunity brings a drawback. Apparently, because myService.Data is an empty, childless object at creation, any Scope that wants to reference its future children have to simple reference the object itself.
For example, $scope.user = myService.data.user throws an error, because that object doesn't exist at declaration. it would appear that my only option is for each controller to simply have $scope.data = myservice.data, and the view for each controller will simply have to use
< ng-model='data'>, with the declarations being something like {{data.user.username}}. I have tested it, and this does work.
My question is this; Is there any way I can get the best of both worlds? Can I have my service update it's data model dynamically, yet still have my controllers access only the part that they need? I? I was feeling quite clever until I realized that all of my Controllers were going to have access to the entire data model... But I honestly can't decide if that is even a huge problem.
Here is my Service:
app.factory('WebSocketService', ['$rootScope', function ($rootScope) {
var factory = {
socket: null,
data: {},
startConnection: function () {
//initialize Websocket
socket = new WebSocket('ws://localhost:2012/')
socket.onopen = function () {
//todo: Does anything need to happen OnOpen?
}
socket.onclose = function () {
//todo: Does anything need to happen OnClose?
}
socket.onmessage = function (event) {
var packet = JSON.parse(event.data);
////Model of Packet:
////packet.Data: A serialised Object that contains the needed data
////packet.Operation: What to do with the Data
////packet.Model: which child object of Factory.data to use
////packet.Property: used by Update and Delete to find a specific object with a property who's name matches this string, and who's value matches Packet.data
//Deserialize Data
packet.Data = JSON.parse(packet.Data);
//"Refresh" is used to completely reload the array
// of objects being stored in factory.data[packet.Model]
// Used for GetAll commands and manual user refreshes
if (packet.Operation == "Refresh") {
factory.data[packet.Model] = packet.Data
}
//Push is used to Add an object to an existing array of objects.
//The server will send this after somebody sends a successful POST command to the WebSocket Server
if (packet.Operation == "Push") {
factory.data[packet.Model].push(packet.Data)
}
if (packet.Operation == "Splice") {
for (var i = 0; i < factory.data[packet.Model].length; i++) {
for (var j = 0; j < packet.Data.length; j++){
if (factory.data[packet.Model][i][packet.Property] == packet.Data[j][packet.Property]) {
factory.data[packet.Model].splice(i, 1);
i--;
}
}
}
}
// Used to update existing objects within the Array. Packet.Data will be an array, although in most cases it will usually only have one value.
if (packet.Operation == "Update") {
for (var i = 0; i < factory.data[packet.Model].length; i++) {
for (var j = 0; j < packet.Data.length; j++) {
if (factory.data[packet.Model][i][packet.Property] == packet.Data[j][packet.Property]) {
factory.data[packet.Model][i] = packet.Data[j]
i--;
}
}
}
}
//Sent by WebSocket Server after it has properly authenticated the user, sending the user information that it has found.
if (packet.Operation == "Authentication") {
if (packet.Data == null) {
//todo: Authentication Failed. Alert User Somehow
}
else {
factory.data.user = packet.Data;
factory.data.isAuthenticated = true;
}
}
$rootScope.$digest();
}
},
stopConnection: function () {
if (socket) {
socket.close();
}
},
//sends a serialised command to the Websocket Server according to it's API.
//The DataObject must be serialised as a string before it can be placed into Packet object,which will also be serialised.
//This is because the Backend Framework is C#, which must see what Controller and Operation to use before it knows how to properly Deserialise the DataObject.
sendPacket: function (Controller, Operation, DataObject) {
if (typeof Controller == "string" && typeof Operation == "string") {
var Data = JSON.stringify(DataObject);
var Packet = { Controller: Controller, Operation: Operation, Data: Data };
var PacketString = JSON.stringify(Packet);
socket.send(PacketString);
}
}
}
return factory
}]);
Here is a Simple Controller that Accesses User Information. It is actually used in a permanent header <div> in the Index.html, outside of the dynamic <ng-view>. It is responsible for firing up the Websocket Connection.
App.controller("AuthenticationController", function ($scope, WebSocketService) {
init();
function init() {
WebSocketService.startConnection();
}
//this is the ONLY way that I have found to access the Service Data.
//$scope.user = WebSocketService.data.user doesn't work
//$scope.user = $scope.data.user doesn't even work
$scope.data = WebSocketService.data
});
And here is the HTML that uses that Controller
<div data-ng-controller="AuthenticationController">
<span data-ng-model="data">{{data.user.userName}}</span>
</div>
One thing you could do is store the data object on the root scope, and set up watches on your various controllers to watch for whatever controller-specific keys they need:
// The modules `run` function is called once the
// injector is finished loading all its modules.
App.run(function($rootScope, WebSocketService) {
WebSocketService.startConnection();
$rootScope.socketData = WebSocketService.data;
});
// Set up a $watch in your controller
App.controller("AuthenticationController", function($scope) {
$scope.$watch('socketData.user', function(newUser, oldUser) {
// Assign the user when it becomes available.
$scope.user = newUser;
});
});

SQLITE DB not being created - PhoneGap

I have a Phonegap (2.1.0) application that onDeviceready creates a DB and populates a table with info.
Running this locally (using the Ripple emulator) on Chrome works. Tables are being created and populated as required.
After installing the build .apk on my Android device my Eclipse logcat shows:
sqlite returned: error code = 14, msg = cannot open file at line 27712 of [8609a15dfa], db=/data/data/<project>/databases/webview.db
sqlite returned: error code = 14, msg = os_unix.c: open() at line 27712 - "" errno=2 path=/CachedGeoposition.db, db=/data/data/<project>/databases/webview.db
Which I believe according to this post here - can be ignored.
However - I also noticed this error in logcat:
sqlite returned: error code = 1, msg = no such table: latest_events, db=/data/data/<project>/databases/webview.db
I have also - through adb shell - confirmed that the DB is not created:
here: /data/data/com.application/databases.
or here: /data/data/com.application/app_databases
So - my code:
if (!window.openDatabase) {
doMessage('Databases are not supported on this device. Sorry','error');
return;
}else{
consoleLog('all good for storage');
var db;
var shortName = 'MyDB';
var version = '1.0';
var displayName = 'MyDB';
var maxSize = 102400;
function errorHandler(transaction, error) {consoleLog('Error: ' + error.message + ' code: ' + error.code);}
function nullHandler(){};
db = window.openDatabase(shortName, version, displayName,maxSize);
consoleLog('starting table creation');
db.transaction(function(tx){
tx.executeSql( 'CREATE TABLE IF NOT EXISTS latest_events (id integer PRIMARY KEY AUTOINCREMENT,EventID integer,EventLocation text,EventName text,EventDateFrom varchar,EventTime timestamp,EventPresentedBy varchar,EventVenue varchar,EventScript text,RequireRSVP varchar)',[],nullHandler,errorHandler);
db.transaction(function(tx){
tx.executeSql('SELECT count(id) as RowCount FROM device_info ', [],
function(tx, result) {
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
consoleLog('rowcount: '+row.RowCount);
if(row.RowCount==0){
tx.executeSql('INSERT INTO device_info (device_name, device_platform, device_uuid, device_os_ver, date_last_used) VALUES (?,?,?,?,?)',[device.name, device.platform, device.uuid, device.version, window.bowman_config.siteDate],nullHandler,errorHandler);
//doMessage('device info row added','notice');
}
}
}
},errorHandler);
},errorHandler,successCallBack('2'));
//doMessage('device info row added','notice');
},errorHandler,successCallBack('1'));
}
To add to my woes - on my logcat I do see the console.log output for "all good for storage", and the "starting table creation" messages.
My errorHandler functions are not returning anything and my successCallBack functions are triggered...but no DB created.
Thanks for the help.
When you pass in successCallBack("2") and successCallBack("1") then you are actually invoking them directly so you may be getting false positives on whether or not success has actually been called. You should provide two separate success call backs or just in-line some functions that call console.log("1") for instance.
Today this issue cost me 3 hours. What I tried:
Rewriting the copy database code.
Deleting the app from the emulator / device
Wiping emulator(s)
Cleaning eclipse
Changing file permissions
Validate a working SQLite database file
I solved the problem by copying the code from a shared Dropbox account to another location and refactoring the code in the Android Manifest and java files with another package name.
The application runs beautifully now, i.e. nothing wrong with the code, but somewhere it's muxed up by Dropbox.
I broke the nested functions up into single functions and 'chained' them based on their success or fail. It's actually been a lot simpler than I thought. RTFM it seemed. Thanks for all the help.
simplified:
var db = window.openDatabase("TheApp", "1.0", "The App", 50000000);
db.transaction(queryDB, errorCB, successCB);
// Query the database //
function queryDB(tx) {
//tx.executeSql('SELECT * FROM table", [], querySuccess, errorCB);
}
function querySuccess(tx, results) {
//do more functions here
}
function errorCB(err) {
console.log("Error processing SQL: "+err.code);
}

Categories

Resources