I have an API <> SPA architecture.
But I don't know exactly where to put translations, specially when errors are send.
I would prefer to just have translated text (i18n etc) at my angular app. The API should only send out english texts.
But I don't know how to
Imagine an Error message like this: (nodejs+restify)
server.get('/api/', function (req, res, next) {
return next(new server.errors.UnauthorizedError('You are not authorized'));
});
This will send a 401 with json in body.
If the API only speaks english, how can I translate it at best at the SPA?
How can I put this to, one of these translation approaches:
<span>{{message | translate}}</span>
or
<span>{{lang.message}}</span>
Of course I use the $translateProvider at Angular and have a locate files with translation in it.
Is there no other way by sending language keys? Like this:
server.get('/api/', function (req, res, next) {
return next(new server.errors.UnauthorizedError('NOT_AUTHORIZED_ERROR'));
});
With this I could have a locate-en.json file like:
{
errors: {
NOT_AUTHORIZED_ERROR: "You are not authorized"
}
}
I find this kind of uncommon.
You have 2 options: either you presume that your client knows upfront all the errors the API can throw. This is usually only the case if both API and the client are implemented by the same team. In that case, let your API return an error object, that contains a code:
{
"code": "unauthorized",
"message": "some reason in english, usefull for debugging/logging"
}
Then use this code to translate the error client side:
<span>{{error.code | translate}}</span>
The second option is to let the API translate the error message. Use this option if the API can evolve (and throw new errors) without the knowledge of the client. Of course, in this case you're limitted to the languages the API supports.
How about put dictonary with code and message into $translateProvider and read the error from local storage, like this:
app.config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('en', {
error : 'messageEN'
})
$translateProvider.translations('de', {
error : 'messageDE'
})
$translateProvider.preferredLanguage('en');
// remember language
$translateProvider.useLocalStorage();
}]);
Related
I have a simple angular resource that I've defined as below:
CompanyService.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
);
I then have a controller that calls that resource passing in a success and fail function:
.controller('companyList', function($scope, CompanyService) {
$scope.companies = CompanyService.query(
function(data) {
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
The rest API is a .NET MVC Web API that is extremely basic. I've configured it to return JSON and it simply returns an array of two objects like below. I've also enabled CORS so my angular app, which is hosted in a different domain, can call the api.
[{ID:1, Name:"TEST1"}, {ID:2, Name:"TEST2"}]
I've tested the REST call using jquery and just straight call through browser. All was functional (including the cross site scripting when calling from my angular app just using a straight JavaScript HTTP call).
When I try to call the api from my controller however, it always ends up in the error function. The error object contains a data property that is always populated with the string "resource is required|resource is required|undefined"
When I check the network I see no call to the values end point. It's as if the call is failing before ever being made.
If I change out the url to point to some sample REST api like https://jsonplaceholder.typicode.com/users/ it works fine and I'm able to see the call to "users" in the network traffic, which makes me think there is something wrong with my C# REST endpoint, however all my tests to call the REST endpoint outside of angular work successfully.
Can anyone help? I can't find anyone reporting this issues before anywhere on the net.
should the code be the one below? i didn't test it, just guess.
myModule.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
)
.controller('companyList', function($scope, CompanyService) {
CompanyService.query(
function(data) {
$scope.companies = data;
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
I ended up rebuilding my angular app from scratch. My first app was from the angular-seed github and had a handful of libraries already added in for testing and other things. One of those things is was was leading to this error as once I started a new project completely from scratch and added in angular and my REST call things worked perfectly. I've already spent too much time working through this so not going to spend any more time identifying exactly what it is but in case anyone else runs into the problem I did want to answer this one and close the book on it.
I'm new to MEAN development, and I'm using bcrypt to encrypt the password from UserSchema (everything works wonders), but since I have to authenticate from the client side I've made a function to the UserSchema:
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
Now.. how do I call this function from the client side?
I'm using Restangular, and I'm trying something like this:
function authLogin() {
if(vm.user && vm.user.email && vm.user.password){
User.getList({ email: vm.user.email }).then(function(user){
user.comparePassword(vm.user.password, function(err, isMatch) {
if (err) throw err;
console.log(vm.user.password, isMatch);
});
});
}}
But then occurs an error saying that user doesn't have the funciton:
user.comparePassword is not a function
So, what is wrong about it? Or it is not possible to call a Schema function directly from the client side?
I believe the answer to your immediate question (if i understand which of your code samples lives to which layer): why is it "not possible to call a Schema function directly from the client side" is that the server and client side javascript run in a completely different context so functions you might declare in server-side models or middleware are not available to your client context.
The general solution to "call a schema function" from the client side with node and javascript frameworks is to POST or GET data to a server-side API endpoint and then you call your schema methods within the API code (or middleware attached to that API endpoint).
I would suggest that you take a look at the https://www.npmjs.com/package/passport passport authentication API module which is a market standard way to handle client authentication that you should be able to pretty easily bolt onto your application.
I am trying to run the IBM Watson's Tradeoff Analytics widget to show the trade-off analytics graph in a webpage. The Tradeoff Analytics API is starting properly but when I submit the problem to the show the graph, I get some undefined error.
Here is the sample code that I am using the run the Tradeoff Analytics Widget.
function errorHandler(payload){
alert(payload.errorMessage);
}
function onShowCompleteCB(payload){
alert('show Tradeoff graph complete');
}
function onStartCB(payload){
alert('sending trade-off problem');
var problem = <problem-json>;
taClient.show(problem, onShowCompleteCB);
}
var options = {
dilemmaServiceUrl : <tradeoff-service-url>,
username : <username>,
password : <password>
};
var taClient = new TradeoffAnalytics(options , document.getElementById('watson_widget'));
var s = taClient.subscribe('afterError', errorHandler);
taClient.start(onStartCB);
I also noticed from javascript debugger that HTTP response to the last request returned the reponse header WWW-Authenticate:Basic realm="IBM Watson Gateway Log-in". Moreover I get the following error in javascript console XMLHttpRequest cannot load . No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '' is therefore not allowed access. The response had HTTP status code 401.
Can somebody help me out with what might be going wrong here?
PS: I have cross checked my username and password and they seem to be working fine through REST based API invocation.
Based on your code, you are trying to use the client widget. You need to have a proxy app that will receive the request and use your username and password.
On your client side you will need something like:
HTML:
<div id='DIV_ID'></div>
JS:
taClient = new TA.TradeoffAnalytics({
customCssUrl: 'https://ta-cdn.mybluemix.net/v1/modmt/styles/watson.css',
dilemmaServiceUrl: '/proxy',
profile: 'basic'
}, 'DIV_ID');
taClient.subscribe('afterError', function onError(){ /* on error */});
taClient.start(function onLoad(){ /* on load */});
}
Server side(nodejs):
var tradeoffAnalytics = watson.tradeoff_analytics({
version: 'v1',
username: '<username>',
password: '<password>'
});
app.post('/proxy', function(req, res) {
tradeoffAnalytics.dilemmas(req.body, function(err, dilemmas) {
if (err)
return res.status(err.code || 500).json(err.error || 'Error processing the request');
else
return res.json(dilemmas);
});
});
Above you will find an example of how to implement the proxy using express and the watson-developer-cloud npm module.
There is a full sample you can look in github
I have a Cloud Endpoints method that looks like this:
//HTTP POST
#ApiMethod(name = "hylyts.insert")
public Hylyt insertHylyt(#Named("url") String url, Hylyt hylyt, User user)
throws OAuthRequestException{
log.info("Trying to save hylyt '"+hylyt+"' with id '"+hylyt.getId());
if (user== null) throw new OAuthRequestException("Your token is no good here.");
hylyt.setArticle(getArticleKey(url, user));
ofy().save().entity(hylyt);
return hylyt;
}
I call it from the Javascript Client Library using this:
gapi.client.hylytit.hylyts.insert({PARAMS}).execute(callback);
Now, if I structure {PARAMS} as suggested in the docs (second example),
{
'url': url,
'resource': {
'hylyt': {
'contentType': 'application/json',
'data': hylyt
}
}
}
I get a null object in the endpoint (not to mention that the whole point of this library is to make these calls simple, which this structure clearly violates).
When I structure {PARAMS} as these answers suggest,
{
'url': url,
'resource': hylyt
}
I get a null object in the endpoint again. The correct syntax is this:
{
'url': url,
'id': hylyt.id
'text': hylyt.text
}
Which just blows my mind. Am I doing this all wrong? Is this a bug? Is it only happening because gapi is also passing the auth token in the background?
Yes, I could use the request syntax instead, but, again, why even use the library if it's just as complex as making the XHRs in pure javascript? I wouldn't mind the complexity if Google explained in the docs why things are happening. But the docs, paraphrased, just say use these methods and the auth, CORS, and XHR magic will happen behind closed doors.
Is the API method correctly recognized as POST method?
The resource parameter which is sent as POST body won't work correctly in a GET request.
The way it looks you are actually sending a GET request with the Hylyt properties in the query string.
To make sure you can change the method annotation to this:
#ApiMethod(name = "hylyts.insert", httpMethod = HttpMethod.POST)
Yup, agreed it's a bug. caused me great pains as well.
So i guess the work around is to create a combined object to pass to your api all named and un named parameters. Rather than hardcode each.. a quick loop might be better.
var param = {};
param["url"] = url;
for (var prop in hylyt) {
param[prop] = hylyt[prop];
}
gapi.client.hylytit.hylyts.insert(param).execute(callback);
That mashing together of parameters / objects can become a slick function if you really want.. but it's a band aid for what I'd consider a defect.
I see in the related question (cloud endpoints resource attribute for transmitting named params & body not working), you actually logged a defect.. Good stuff. Though there still appears no movement on this one. fingers crossed for someday!
The bug has been resolved. The correct syntax is
gapi.client.hylytit.hylyts.insert({url: url}, hylyt).execute(callback);
I found it is very hard to deal with flash message returned from backend when working with single-page-application.
Say there are five actions:
login, logout,search,add,delete.
each action emits messages(successful or failed,could be more). in a Non-SPA, it is easy enough to do something like backend.getFlash() in the template to get the message stored in session.
However, in SPA, if the results are returned in JSON, and of course backend can pass the message to a js framework, say angularjs. But it is really tedious and not flexible at all.
Does anyone have any idea or experience?
Thanks
From your question I understood that what you really want is to have some messages for every actions, say for delete you need to display a message like "Deleted Successfully" and you don't want this message to be send along with the result set from your backend.
In AngularJS you can achieve this by having a service for showing the messages.
Here is an example;
Create a service like this in AngularJS
app.factory('FlashService', function ($rootScope) {
return {
show: function (message) {
$rootScope.flash = message;
return message;
},
clear: function () {
$rootScope.flash = '';
return null;
}
};
});
Here FlashService is the name of the service you are creating, don't bother about the name you can give any name of your choice.
To use this service inject it anywhere you want like;
var app = angular.module("your app name", []);
app.controller("MyCtrl", function(FlashService) {
FlashService.show("I am here");
});
And use it in your html like
<div id="flash">{{flash}}</div>