I'm using an x-auth-token generated by spring session to maintain communication, this is fine for ajax requests.I have a problem though, I have files that are not currently being fetched by ajax.
These files are user uploaded content, pdfs, html with css and fonts, basically things the browser can render, so making an ajax force download button isn't an option (though maybe there's a way to do this to view them that I'm not thinking of). Currently they are just being requested by a browser GET via <a href links, so no header is included.
I am using AngularJS. What's the best way to address this issue?
I know this question is a couple months old, but I ran into this problem with AngularJS when I first moved to using Spring Session also. It's a little hacky, but it essentially uses AngularJS's HTTP to retrieve the document and save it in a BLOB in the Browser. Then, a hidden <a> tag is created with a link to the BLOB and the link is clicked programmatically. I have an AngularJS service called 'saveBlob' that handles this:
app.service('saveBlob', ['$http', function ($http) {
return {
download: function(url, fileName) {
$http.get(url, { responseType: 'arraybuffer' }).then(function(response) {
var blob = new Blob(
[ response.data ],
{ type: response.headers('Content-Type') }
);
var objUrl = window.URL.createObjectURL(blob);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = objUrl;
if (fileName) {
a.download = fileName;
}
else {
var regex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g;
var match = regex.exec(response.headers('Content-Disposition'));
var fn = match[1];
a.download = fn.substring(1, fn.length - 1);
}
a.click();
window.URL.revokeObjectURL(url);
});
}
}
}]);
The 'saveBlob' service is then called by a directive called 'httpHref':
app.directive('httpHref', ['$http', 'saveBlob', function ($http, saveBlob) {
return {
link: function ($scope, elem, attrs) {
function revokeObjectURL() {
if ($scope.objectURL) {
URL.revokeObjectURL($scope.objectURL);
}
}
$scope.$watch('objectURL', function (objectURL) {
elem.attr('href', objectURL);
});
$scope.$on('$destroy', function () {
revokeObjectURL();
});
attrs.$observe('httpHref', function (url) {
elem[0].onclick = function() {
saveBlob.download(url);
};
});
}
};
}]);
I adapted this code from This GitHub Repository (which is licensed under the MIT License), so thank you to the contributors of that project for helping me with this!
Related
I want to send headers each time for CRUD operation from factory side.
Here is my factory
var appangular.module("LifeStyleFactModule",["ngResource"]);
app.constant("RES_URL", "http://localhost:9090/")
app.factory("CategoryFactory",function($resource,RES_URL){
var categoryinfo;
var categoryresource=$resource(RES_URL+"category/:id",{"id":"#id"},{update:{method:"PUT"}});
return{
getcategory:function(){
categoryinfo=categoryresource.query();
return categoryinfo;
},
addcategoryItem:function(categoryItem){
var category = new categoryresource(categoryItem);
category.$save(function(respdata){
categoryinfo.push(respdata);
},function(respdata){
});
},
deletecategoryItem:function(idx,id){
var category=new categoryresource({"id":id});
category.$delete(function(){
categoryinfo.splice(idx,1);
},function(){
})
},
updatecategoryItem:function(categoryItem,idx){
var category=new categoryresource(categoryItem);
category.$update({"id":categoryItem._id},function(data){
categoryinfo[idx]=data;
},function(){
})
}
}
})
the above functionality is working well. Now i want to send the token in the headers. How can i do that.
I have tried to do it by the following way
var categoryresource=$resource(RES_URL+"category/:id",{"id":"#id"},{update:{method:"PUT"},headers:{"token":"#token}});
but not getting how to send the token for CRUD operation.
Is procedure is correct, if so how can i send tokens.
Else let me know the way.
Instead of above method i tried the following way as
$resource(RES_URL+"category",{},{query:{method:"get",isArray:true,headers:{"token":token}}}).query({},function(res){});
this is working but the procedure for the first procedure.
Please after answering mark it as duplicate or down vote
dont say ( / { such things are missing.
The best solution as to me is to use interceptor. Here is a way to send token in headers, I've used in one of my projects.
angular
.module('app.core')
.config(config);
config.$inject = ['$httpProvider'];
function config($httpProvider) {
$httpProvider.interceptors.push(interceptor);
}
interceptor.$inject = ['$q', '$injector', 'AuthModel'];
function interceptor($q, $injector, AuthModel) {
return {
request: function (config) {
config.headers.Authorization = AuthModel.token;
return config;
},
responseError: function (rejection) {
}
};
}
Added a jsfiddle to demonstrate
https://jsfiddle.net/Sergey_Mell/c47js1zc/
Just click the Send button and check the request headers in developer tools
I really do not understand how to handle URLs with queries appended to it.
I have endpoints that accept several parameters like:
?max_length=50,
?min_length=1,
?active=true,
?only_today=true,
?etc...
Via AngularJS how can I set those value dynamically only if the user has checked for those values?
Actually I'm just building an object {} appending those parameters when the $scope is not null. But I don't think it is a good idea.
Same for NodeJS and MongoDB...
How can I get the correct object based on the query string on the URL?
What I'm doing here as well is to split up the URL and checking for the words, etc... I'm sure there is a better way and I can not find it in both documentations and wondering to bigger and bigger URL parameters it start to be hell.
I know this is a real low level question but I don't really understand how to handle it.
Thanks
You can use the $location service for that.
https://docs.angularjs.org/api/ng/service/$location
You can use $resource to easily map your endPoints to your services. You should map your params to what is expected in your api. And if you have conditional parameters, you need to handle undefined params in your backend and ignore these params. For mapping your endpoints in nodeJS check out Restify
For example:
angular.module("myApp", []).factory("myFactory", function($resource) {
var YourResource = $resource('/rest/yourResource');
var factory = {
retriveMyResources: function(paramsQuery) {
return YourResource.query(paramsQuery).$promise;
}
};
return factory;
}).controller("myCtrl", function($scope, myFactory) {
myFactory.retrieveMyResources({
maxLength: $scope.maxLength,
sortBy: $scope.sortBy
}).then(function(result) {
$scope.result = result
})
})
Your node server
//App.js you initialize your server, and include your route files
(function() {
var restify = require("restify");
var server = restify.createServer({
name: "test-server"
});
server.pre(restify.CORS());
server.use(restify.gzipResponse());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.use(restify.jsonp());
require("./routes/your_resource_route.js")(server);
server.listen("1921", function() {
console.log('%s listening at %s environment: %s', server.name, server.url, process.env.NODE_ENV);
});
})();
Example Route file
module.exports = function(server) {
var yourResourceService = require("services/your_resource_service.js");
server.get("rest/yourResource",
function(req, res, next) {
return yourResourceService.findResources(req.params.maxLength, req.params.sortBy).then(function(resources) {
res.send(200, resources);
next();
}).catch(function(err) {
res.send(500, err);
next();
}).done();
}
);
}
And your service file
module.exports = function(app_context) {
var exampleService = {
findItems: function(maxLength, sortBy) {
var sortObject = {};
sortObject[sortBy || DEFAULT_SORT] = -1;
return Q(brandMongooseCollection.find({}).sort(sortObject).limit(maxLength || DEFAULT_MAX_LENGTH).lean().exec());
}
};
return exampleService;
};
I'm using Sails 0.11 in my back-end, and angularjs in the front-end.
I have a TwitterController in sails with the following code, to open a stream with the Twitter Streaming API (this uses the node module twit):
var Twit = require('twit');
var T = new Twit(sails.config.twit);
var stream = T.stream('statuses/filter', {
track: ['apple']
});
module.exports = {
open: function(req, res) {
if (!req.isSocket) {
return res.badRequest();
}
var socketId = sails.sockets.id(req.socket);
stream.start();
stream.on('tweet', function(tweet) {
sails.log.debug('Tweet received.');
sails.sockets.emit(socketId, 'tweet', tweet);
});
}
};
In my front-end (with the angular-sails module):
$sails.get('/twitter/open').then(function(resp) {
console.log(resp.status);
}, function(resp) {
alert('Houston, we got a problem!');
});
This of course reaches my back-end controller, and the streaming starts, but how do I listen to the
sails.sockets.emit(socketId, 'tweet', tweet);
issued by the server?.
I'd appreciate any help here!.
Sails library creates a global variable "io", so you can use also:
io.socket.on("tweet", function(data){})
Following wZVanG's answer. This is how I did it, using the angular-sails angular module.
$sails.get('/twitter/open').then(function(resp) {
console.log(resp.status);
}, function(resp) {
alert('Houston, we got a problem!');
});
$sails.on('tweet', function(message) {
console.log(message);
});
That logs every tweet received to the browser's console.
I have implemented resources in my single page angular app which fires to my REST client server. I have made different services for each resource. Now my REST server is sending a value in response header, now I want to know a proper way where I can retrieve that value from headers.
My service code:
app.service('$job', function($resource) {
var job = $resource(service_base_url+'jobs.json/:id');
return job;
});
My controller which is getting headers:
app.controllerProvider.register('JobPostsController',['$scope','$job', function($scope, $job) {
$scope.jobs = {};
$scope.job_titles = {};
$job.query(function(jobs,responseHeaders){
var headers = responseHeaders();
some_function(headers.user);
$scope.jobs = jobs.jobs;
});
}
]);
I am getting headers in my above code, but I don't want to inject it in all controllers. So is there a proper way to do it? Some single config code which will run for all future resources request or some kind of event which can be only triggered when successful resource response with 200 OK
Try interceptor.
I don't know exactly your logic. You could register a global interceptor which intercepts all requests:
angular.module('App', [])
.config(function ($httpProvider){
$httpProvider.interceptors.push(function() {
return {
'response': function(response) {
var headers = response.headers();
some_function(headers.user);
return response;
}
};
});
});
or just register an interceptor which runs only for all requests of this query.
app.service('$job', function($resource) {
var job = $resource(service_base_url+'jobs.json/:id',{}, {
'query': {
method:'GET',
isArray:true,
interceptor: {
'response': function(response) {
var headers = response.headers();
some_function(headers.user);
return response;
}
}
}
});
return job;
});
Side notes:
Should not use $ prefix for your service name as it's reserved for angular, it may conflict with angular future versions.
I guess you need .factory instead of .service
You can set up the service in a run block like:
angular.module('myApp', [])
.run(['$rootScope', '$job',function ($rootScope, $job) {
$rootScope.jobs = {};
$rootScope.job_titles = {};
$job.query(function(jobs,responseHeaders){
var headers = responseHeaders();
some_function(headers.user);
$rootScope.jobs = jobs.jobs;
});
}]);
The only drawback your service is global to the app
I would go with a base service factory. This would allow you to have common service related functionality in one place
app.factory('ServiceBase', function () {
function ServiceBase() {
this.responseHeaders = function responseHeaders(resp){
// todo
};
}
return ServiceBase;
});
app.service('$job', function($resource, ServiceBase) {
var service = function () {
// $job related functions here
};
angular.extend(service, new ServiceBase());
return service;
});
Now anything in the ServiceBase is accessible to the controller and to the service. This allows you to have common functionality, has no new injection dependencies (on the controller), and is easy to extend further.
I think angulrjs response interceptors can help u for this.
I'm attempting to use ngResource to query a REST API. I need to specify my API key in a custom header. I've tried it like so:
angular.module('ApiService', ['ngResource'])
.factory('Api', ['$resource', function($resource) {
this.apiEndpoint = '';
this.apiKey = '';
return {
init: function(apiEndpoint, apiKey) {
this.apiEndpoint = apiEndpoint;
this.apiKey = apiKey;
},
get: function(collection) {
return $resource(this.apiEndpoint + 'api/1/' + collection, {},
{
get: {
method: 'JSONP',
headers: {'api_key': this.apiKey},
params: {callback: 'JSON_CALLBACK'}
}
}
).get();
}
};
}]);
which I then use in my controller like:
app.controller('MyCtrl', function ($scope, Api, ENV) {
Api.init(ENV.apiEndpoint, ENV.apiKey);
var widgets = Api.get('widgets');
});
My custom header isn't set when I inspect the XHR. Also, why will the XHR not run until I call an empty .get() after the initial $resource:get() method?
I've also tried to set the headers in $httpResource directly:
.config(function($httpProvider) {
$httpProvider.defaults.headers.get = {'api_key': 'abc123'};
})
but this still doesn't set the custom header when I inspect the network request. What am I missing?
This issue is, of course, that I was using JSONP in this request, which doesn't include the ability to craft headers when making a request. See how to change the headers for angularjs $http.jsonp.
Specifically, JSONP simply includes a <script> tag at the bottom of the DOM to load cross-domain javascript, so it's up to your browser to send the default headers.