I'm a noob to AngularJS and trying to set up a simple webapp in order to test, if the framework suites our needs. I've read the API reference and did the tutorial. Unfortunately the way path and locations are set and handled on $resource is not quite well explained there.
The problem I am facing is that I always get a 404 when I give $resource the relative path as described in the tutorial.
Here is my set up:
I have cloned the AngularJS phone cat app and threw away their phone stuff but made use of their structure to bring in my own logic. My index.html looks as follows:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>testapp</title>
<link rel="stylesheet" href="css/bootstrap.css">
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
<script src="js/services.js"></script>
</head>
<body ng-app="testapp">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="#">Testapp using AngularJS</a>
</div>
</div>
<div ng-view></div>
</body>
First trouble was that AngularJS doesn't seem to give any advice on how to oauth to a RESTful API, and their tutorial only gives a "dream scenario" of a server delivering hard-coded JSON, so I have hacked into their web-server.js in order to let the node.js server handle the API calls and return the resulting JSON. For oauth I did so successfully, yet for further API calls, it doesn't work.
Here's my hack:
StaticServlet.prototype.handleRequest = function(req, res) {
var self = this;
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
return String.fromCharCode(parseInt(hex, 16));
});
var parts = path.split('/');
if (parts[parts.length-1].charAt(0) === '.')
return self.sendForbidden_(req, res, path);
/* Hack for RESTful API calls. */
var post = '';
var query = qs.parse(req.url);
if (req.method === 'POST') {
var body = '';
req.on('data', function(data) {
body += data;
});
req.on('end', function () {
post = JSON.parse(body);
console.log(post);
// method name is part of the parameters
// apiRequests is the module handling the API requests
apiRequests[post.method](post, res);
});
} else if (query.api === 'api') {
var query = qs.parse(queryString);
// method name is part of the parameters
// apiRequests is the module handling the API requests
apiRequests[query.method](queryString, res);
} else {
fs.stat(path, function(err, stat) {
if (err)
return self.sendMissing_(req, res, path);
if (stat.isDirectory())
return self.sendDirectory_(req, res, path);
return self.sendFile_(req, res, path);
});
}
}
It's really just an ugly hack, but anyways. The login POST request is delegated to an oauth call and the api GET requests are delegated to the proper api request methods, the rest is handled the same way as web-server.js did before.
The trouble is, the handleRequest method is never called.
Here is my client side service code making the call:
return {
fetchCommunications : function ($scope) {
var parameters = {
'api': 'api',
'method': 'communications',
'bearer':userData["access_token"],
'chiffre':userData["chiffre"]
}
var resource = {
r : $resource('communications', {}, {
query: {method:'GET', params:parameters}
})
}
var d = resource.r.query();
console.log("Communications: " + d);
return d;
}
And this is the error message from the server console:
GET /app/communications?api=api&bearer=d6a348ea-0fe5-46fb-9b5e-13d3343c368d&chiffre=0000006F&method=communications Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22
404 Not Found: /app/communications
This is exactly the path I'd expect to be called - but why is it 404?
I can avoid the 404 by configuring $resource like this:
var resource = {
r : $resource('index.html#/communications', {}, {
query: {method:'GET', params:parameters}
})
Which doesn't work either, since everything after the hash is omitted from the path. Besides, why would I need to have index.html within the path?
I have the feeling that I am missing something pretty obvious here and am therefore doing something pretty dumb. Anyone with any ideas? Is it something related to the configuration of node.js (never had this trouble before)?
Many thanks in advance!
Related
I'm having issues with developing my Google Chrome extension. I need some specific npm modules in order to execute my code so I looked into Browserify. I followed all the steps without issue but the code still produces errors when run. The screenshot is attached below.
Error when Chrome extension is only loaded
All my files are located in the same project folder (popup.html, popup.js, bundle.js, etc.). I only have one html file and one javascript file (excluding bundle.js).
Here is my popup.html code:
document.addEventListener('DOMContentLoaded', function() {
var convertMP3Button = document.getElementById("getLinkAndConvert");
convertMP3Button.addEventListener("click", function() {
chrome.tabs.getSelected(null, function(tab) { // 'tab' has all the info
var fs = require('fs');
var ytdl = require('ytdl-core');
var ffmpeg = require('fluent-ffmpeg');
var ffmetadata = require("ffmetadata");
var request = require('request');
console.log(tab.url); //returns the url
convertMP3Button.textContent = tab.url;
var url = tab.url;
var stream = ytdl(url);
//.pipe(fs.createWriteStream('/Users/nishanth/Downloads/video.mp4'));
// Helper method for downloading
var download = function(uri, filename, callback){
request.head(uri, function(err, res, body){
request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
});
};
ytdl.getInfo(url, function(err, info) {
console.log("INFO: " + JSON.stringify(info, null, 2));
var process = new ffmpeg({source:stream})
process.save('/Users/nishanth/Downloads/' + info.title + '.mp3').on('end', function() {
console.log("PROCESSING FINISHED!");
download(info.thumbnail_url, "/Users/nishanth/Downloads/image.jpg", function() {
console.log("DOWNLOADED IMAGE");
var options = {
artist: info.author,
attachments: ["/Users/nishanth/Downloads/image.jpg"]
};
ffmetadata.write('/Users/nishanth/Downloads/' + info.title + '.mp3', {}, options, function(err) {
if (err)
console.error("Error writing cover art: " + err);
else
console.log("Cover art added");
});
});
});
});
});
});
});
<!doctype html>
<html>
<head>
<title>Youtube Music</title>
<script src="bundle.js"></script>
<script src="popup.js"></script>
</head>
<body>
<h1>Youtube Music</h1>
<button id="getLinkAndConvert">Download Song Now!</button>
</body>
</html>
It would be great if I could find out the reason why I have not been able to properly integrate browserify in order to use the npm modules.
Browsers don't allow you to access the file system, instead they usually have some storage mechanisms of their own (cookies, localstorage, or a browser-specific system like chrome.storage). Browserify has no way of getting around this, and doesn't provide a shim for require('fs'). Instead of writing directly to disk you'll need to have your app provide a downloadable version of your files, which the user will then have to save manually. If you don't need the files to be accessible outside the extension you can use the api I linked earlier, or drop in something like browserify-fs which creates a virtual file system in the browser's storage.
I am trying to establish REST connection between node (middleware) and angular (UI). However, the json is displayed on the browser rather than being routed via the angular controller/html
Node/Express router.js
router.get('/members', function(request, response){
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Methods", "GET, POST");
response.setHeader('Content-Type', 'application/json');
var dbdata = [];
var str;
db.get('_design/views555/_view/app_walltime', function (err, body) {
if (!err) {
body.rows.forEach(function(doc) {
dbdata.push({name: doc.key, walltime:doc.value});
});
console.log(dbdata);
response.json(dbdata);
Angular controllers.js
'use strict';
var phonecatApp = angular.module('phonecatApp', []);
phonecatApp.config(['$httpProvider', function($httpProvider, $routeProvider, $locationProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
phonecatApp.controller('PhoneListCtrl', function ($scope, $http, $templateCache) {
alert('asdsad');
$scope.list = function() {
alert('hereh');
var url = 'http://192.168.59.103:8072/members';// URL where the Node.js server is running
$http.get(url).success(function(data) {
alert(data);
$scope.phones = data;
});
};
$scope.list();
});
html - testangular.js
<html ng-app="phonecatApp">
<head>
<meta charset="utf-8">
<title>My HTML File</title>
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.css">
<script src="/bower_components/angular/angular.js"></script>
<script src="/bower_components/angular-route/angular-route.min.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<ul class="phones">
<li ng-repeat="phone in phones | filter:query">
{{phone.name}}
<!--<p>{{phone.walltime}}</p> -->
</li>
</ul>
</div>
</div></div>
What is see is the following on the browser
[{"name":"app1","walltime":"1050"},{"name":"app2","walltime":"30"}]
I seem to be missing some configuration to let node and angular communicate. Please let me know what I am doing wrong.
What you see on the browser's window is a JSON object, that is the result of your request.
With the line $scope.phones = data;, you're simply assigning to $scope of Angular the data object, without actually parsing it.
The result is that Angular is not able to understand in the ng-repeat directive what actually {{phone.name}} and {{phone.walltime}} are and the JSON string is shown.
In order to have the ng-repeat directive working as expected, you have to parse first the JSON object, process it (creating for example a custom object and assigning its properties) and then assign it to $scope object.
You could perform the parse using something like
JSON.parse(data);.
Please have a look at this question for further information.
Or even using the Angular's builtin function angular.fromJson(data).
An example could this:
$http.get(url).success(function(data) {
alert(data);
$scope.phones = angular.fromJson(data);
});
};
Test this :
var url = 'http://192.168.59.103/members';
$http.get(url).then(
function(response) {
console.log('success',response)
},
function(data) {
// Handle error here
})
I want to receive on an HTML5 website JSON from a PostgreSQL database. So, on the server side I use node-postgres module for DB connection and also express module for communication.
The problem is that in the html i am not seeing any alert when getting the data from the server. The alert isn't even thrown.
this is how my code is so far, for anyone that could help:
serverside
var express = require('express');
var app = express();
app.get('/data', function(req, res){
var pg = require('pg');
var conString = "postgres://postgres:postgres2#localhost/spots";
var client = new pg.Client(conString);
client.connect(function(err) {
if(err) {
res.send('could not connect to postgres');
}
client.query('SELECT * from spots_json where id=3276', function(err, result) {
if(err) {
res.send('error running query');
}
res.set("Content-Type", 'text/javascript'); // i added this to avoid the "Resource interpreted as Script but transferred with MIME type text/html" message
res.send(JSON.stringify(result.rows[0].json));
client.end();
});
});
});
app.listen(3000);
clientside
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"></meta>
<meta charset="utf-8"></meta>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js" ></script>
<script>
$.get('http://localhost:3000/data?callback=?',{}, function(data){
alert(data.type);
},"json");
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
The client is now executed on http://localhost:8888/prueba/prueba.html
Im getting a js with the following Response:
"{\"type\":\"Point\",\"coordinates\":[-2.994783,43.389217]}"
The Response can be seen in the following screenshot:
result.rows[0].json is not an object, it is a string. You don't need to stringify it:
res.send(result.rows[0].json);
Edit:
If you use two servers on different ports you will need to use JSONP. jQuery makes this simple on the client side, but you will need to implement it in your server (example):
if(req.query.callback) {
res.send(req.query.callback + '(' + result.rows[0].json + ');');
} else {
res.send(result.rows[0].json);
}
By the way, you need to return if you encounter an error in one of your callbacks to prevent subsequent code from being executed.
if(err) {
res.end('error message');
return;
// Or shorter: return res.end('error message');
}
I am very new to node.js and I want to receive on an HTML5 website JSON from a PostgreSQL database. So, on the server side I use node-postgres module for DB connection and also express module for communication. The PostgreSQL query is returning a JSON object.
Server-side:
var express = require('express');
var app = express();
app.get('/data', function(req, res){
var pg = require('pg');
var conString = "postgres://postgres:postgres2#localhost/spots";
var client = new pg.Client(conString);
client.connect(function(err) {
if(err) {
res.send('could not connect to postgres');
}
client.query('SELECT * from spots_json where id=3276', function(err, result) {
if(err) {
res.send('error running query');
}
res.send(JSON.stringify(result.rows[0].json));
client.end();
});
});
});
app.listen(3000);
Client-side:
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js" ></script>
<script>
$.get('http://localhost:3000/data',{}, function(data){
alert(JSON.parse(data));
},"json");
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
If I navigate to http://localhost:3000/data on the browser I get:
{\"type\":\"Point\",\"coordinates\":[-2.994783,43.389217]}
So I see that the server is sending the stringified JSON properly, but on the client I always get null data. I must have some misconception.
Ok this is how my code is so far, for anyone that could help:
serverside
var express = require('express');
var app = express();
app.get('/data', function(req, res){
var pg = require('pg');
var conString = "postgres://postgres:postgres2#localhost/spots";
var client = new pg.Client(conString);
client.connect(function(err) {
if(err) {
res.send('could not connect to postgres');
}
client.query('SELECT * from spots_json where id=3276', function(err, result) {
if(err) {
res.send('error running query');
}
res.set("Content-Type", 'text/javascript'); // i added this to avoid the "Resource interpreted as Script but transferred with MIME type text/html" message
res.send(JSON.stringify(result.rows[0].json));
client.end();
});
});
});
app.listen(3000);
clientside
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"></meta>
<meta charset="utf-8"></meta>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js" ></script>
<script>
$.get('http://localhost:3000/data?callback=?',{}, function(data){
alert(data.type);
},"json");
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
The client is now executed on http://localhost:8888/prueba/prueba.html
Im getting a js with the following Response:
"{\"type\":\"Point\",\"coordinates\":[-2.994783,43.389217]}"
The Response can be seen in the following screenshot:
https://www.dropbox.com/s/zi4c5pqnbctf548/pantallazo.png
But now the alert isn't even shown...
I think i need some light with this.
The data is already an object when you get it, not a string. So JSON.parse fails because you gave it an object when it was expecting a string. To verify, change
alert(JSON.parse(data));
to
alert(data.type);
and you should get "Point"
The reason you already have an object is because of the "json" parameter you provided to $.get. If you change that to "html" you will get a string back instead which you could then parse out into a JSON object.
I think you should not try to stringify your result when you put it in the response object.
Just put it entirely it will automaticaly that way :
res.set("Content-Type", 'application/json');
res.json(result.rows[0].json);
That is the right way to send information through REST APIs.
Then in your client side code, I don't know how it works but it should accept json natively.
It's my first question here, sorry in advance if there's a discussion of what i'm going to ask, already. I'm new to angularjs and I'm pretty sure this is not a big problem, but I don't understand what I have to do. I'm working with API teech.io and I would like to get some experience to learn how to use them. My problem is this: make a GET request via http (each user has an app key and an app Id), for example to materials. The documentation of materials says this:
A GET to /materials/:id fetches the material object.
GET /materials/:id HTTP/1.1 Host: api.teech.io Teech-Application-Id:
[app ID] Teech-REST-API-Key: [API key]
<!DOCTYPE html>
<html>
<head>
<!-- INCLUDE REQUIRED THIRD PARTY LIBRARY JAVASCRIPT AND CSS -->
<script type="text/javascript" src="js/angular.min.js"></script>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/bootstrap-responsive.min.css">
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="teechpadController2.js"></script>
</head>
<body>
the app.js is this one
var app = angular.module('TeechTest',[]);
The teechpadController is this
teechpad.factory('$api', function($http) {
var defaults = {
method: null,
url: null,
headers: {
'Teech-REST-API-Key': api.teech.io.apikey,
'Teech-Application-Id': api.teech.io.appid.
}
};
var req = function(method, url, config) {
defaults.method = method;
defaults.url = 'http://api.teech.io' + url;
var h = angular.extend({}, defaults, config);
console.log(heart);
return $http(heart);
};
return {
get: function(url, config) {
return req('GET', url, config);
},
put: function(url, data, config) {
defaults['Content-Type'] = 'application/json';
defaults['data'] = data;
return req('PUT', url, config);
},
post: function(url, data, config) {
defaults['Content-Type'] = 'application/json';
defaults['data'] = data;
return req('POST', url, config);
},
delete: function(url, config) {
return req('DELETE', url, config);
}
}
});
What I understood till now is that teechpadcontroller re-define the PUT-POST-DELETE-GET methods. Now, How can I use, for example, get method in my controller? Or Should I create an other controller and then use $app there? Sorry again for everything, I'm very new here and in angularjs. Last but not least, I work with JSON object(I think it was clear already)
In your Controller (where you inject this factory) you could use this factory called $api.
The exposed functions to this $api is the four functions returned in the returncaluse:
return {
get: function(url, config) {
...
},
put: function(url, data, config) {
...
},
post: function(url, data, config) {
...
},
delete: function(url, config) {
...
}
}
so in your own controller you could use it something like this:
JavaScript
angular.module('myModule',['TechTest']).controller('myCtrl',function($api){
var customUrl = "/PATH/TO/ENDPOINT",
customConfig = {}; // might not be needed.
$api.get(customUrl, customConfig).success(function(data){
$scope.apiResult = data;
});
})
HTML (which needs to know about your controller per usual)
<!-- include scripts -->
<body ng-app="myModule">
<div ng-controller="myCtrl">
<div>{{apiResult}}</div>
</div>
</body>
I hope I did not missunderstood your quetion.
Now, How can I use, for example, get method in my controller?
You can use your '$api' inside any controller you define by adding it to the injectables of that controller through
var app = angular.module('TeechTest',[]).factory('$api', function($http){ ... });
var controller = app.controller('ctrl', ['$api', function($api){ ... }]);
and then call your 'get' method by going: $api.get( ... ); inside your controller.
Watch out, you've defined 'h' and 'heart' on two different lines, this might just be a typo but it might trip you up!
Your function call to $api.get( ... ); will return asynchronously and you will get the result inside either:
$api.get( ... ).success(function(response){ console.log(response); }); //success
$api.get( ... ).error(function(response){ console.log(response); }); //failure
This is a deferred callback - look into the promise API for more in depth details. You may also want to look into resetting the defaults object every time so you guarantee you're working with a clean object each function call.
Any incorrectness then say and I'll update my answer :)