Translate the messages that come from the server - ruby-on-rails

I am using a hybrid application with vuejs and ruby on rails, I want to translate the error messages that come to me from the server, for the titles I could easily do them with a method but for the error messages I don't know how to translate them.
these are my errors taken from the server, as you can see the title of the error I managed to translate it, but the message I don't know how to do. For example End date i want to translate the message.
my code to get the errors:
<div v-if="getDataError">
<div v-for="(_errors, key) in getDataError">
<b-alert
v-for="error in _errors"
show
variant="danger"
>
<h2>{{ formatKey(key) }} :</h2>
<li>{{ error }}</li>
</b-alert>
</div>
</div>
my method to translate the title message :
methods: {
formatKey (key) {
return this.$t('vue.' + key.replace('contract_data.', ''))
}
},

As I can see you are getting three type of errors from the server (API response) :
Unprocessable Entity
can't be blank
teammate_contract_end_after_company_contract
Approach : You can assign these errors in to language object like below.
const messages = {
en: {
error: {
'code422Msg': 'Unprocessable Entity',
'requiredFieldMsg': 'can't be blank',
'endDateMsg': 'teammate_contract_end_after_company_contract'
}
}
}
Now, Instead of returning the error text from API, you can return error.code422Msg, error.requiredFieldMsg, error.endDateMsg based on the validations.
And then finally, translate these dot notation strings in template or in script using $t('error.code422Msg'), $t('error.requiredFieldMsg'), $t('error.endDateMsg')
Hope it will work! Thanks

Related

My Stimulus checkbox script in Rails 7 doesn't work

I'm learning stimulus and trying to get add a checkbox feature where you can mark an order as complete from the show page without using a form. I followed this tutorial, but am not getting the correct results. The checkbox does nothing when clicked and unchecks when refreshed; however if I manually set the complete attribute to true, the checkbox is automatically checked when loading the page, as it should.
I have a model "Order" with a boolean attribute "complete". Here's my show.html.erb section
<tr data-controller="todo" data-todo-update-url="<%= order_path(#order.id) %>">
<td>
<div>
<input type="checkbox"
data-action="todo#toggle"
data-target="todo.completed"
<% if #order.complete %> checked <% end %> >
</div>
</td>
</tr>
Here's my stimulus todo_controller
import { Controller } from "#hotwired/stimulus"
export default class extends Controller {
static targets = [ "completed" ]
toggle(event) {
// Inside the toggle(event) function, let’s start by getting the value of the checkbox,
// and put it into a FormData object
let formData = new FormData()
formData.append("#order[complete]", this.completedTarget.completed);
// Let’s post that data to the "update-url" value we set on the Todo row.
// We’ll set the method to PATCH so that it gets routed to our todo#update on our controller.
// The credentials and headers included ensure we send the session cookie and the CSRF protection token and
// and prevent an ActionController::InvalidAuthenticityToken error.
fetch(this.data.get("update-url"), {
body: formData,
method: 'PATCH',
dataType: 'script',
credentials: "include",
headers: {
"X-CSRF-Token": getMetaValue("csrf-token")
}
})
// We can take the Response object and verify that our request was successful.
// If there was an error, we’ll revert the checkbox change.
.then(function(response) {
if (response.status != 204) {
event.target.complete = !event.target.complete
}
})
}
}
Can someone tell me where my code is going wrong?
This is more a long comment than a solution but few things I see :
You create an empty form and append a single input with name
"#order[complete]" though your Stimulus controller is Javascript
and has no knowledge of # the such way you use in Ruby. Also params
names are usually model[field] then I think you don't need the #.
"order[complete]" should be fine.
Also you grab the value from a specific target for the aforementionned value with this.completedTarget.completed. Should you not rather pick the value of the input field ? and rather grab this.completedTarget.value or maybe the checked status this.completedTarget.checked
You are getting the URL to your fetch from a data attribute. I am not a stimulus expert but it doesn't look like anything Stimulus related. As of now you have written it this.data.get("update-url") but in regular javascript, something like this.element.dataset.todoUpdateUrl should work.
And just to be sure there is no confusion to about where you will
call this , just declare it at the top of your Stimulus methd like
this : var backUrl = this.element.dataset.todoUpdateUrl. And fill the url to your fetch as just fetch(backUrl,...
you pass the formData directly to your fetch body. If this doesn't work, try to stringify it and extract the entries like : JSON.stringify(Object.fromEntries(formData)). Also I am not too sure about the dataType: 'script', you may just omit that alltogether.
There may be other problems that I don't see. Also when you are dealing with JS, don't only look to your Rails console, especially if nothing hits the backend. Open the developper / inspect tool in your browser and monitor the console there, you should see all the XHR (async) requests made to your app.
If nothing happens, then your fetch is not firing and there need to be more investigations made..

How to fix parsing errors in form POST request in Rocket?

I am making a very simple web app using the rust Rocket framework. I have a very simple HTML file that has a form, as follows:
<form action="/search" method="post" accept-charset="utf-8">
Search Term:<input type="text" name="searchterm">
<input type="submit" value="search">
</form>
Next, here are my rocket functions to deal with the requests. I have a get function that spits out index.html when accessing "/", then for my form, I have the following functions:
#[derive(FromForm)]
pub struct Request<'r> {
payload: &'r RawStr,
// we can add more if we want later on, for other form options...
}
#[post("/search", data = "<data>")]
pub fn process(data: Form<Request>) -> Result<Redirect, String> {
if data.payload == "Hello!" {
Ok(Redirect::to("/search/Hello"))
} else {
Err(format!("Unknown search term, '{}'.", data.payload))
}
}
Then, this is to response to the GET requests:
#[get("/search/<term>")]
pub fn response(term: &RawStr) -> String {
format!("You typed in {}.", term)
}
Like I said, very simple, very barebones, just trying to tiptoe into both Rust and Web Apps at the same time. I do not have much experience in either. My issue is, when using the field presented to the user in my html file, the server returns an error:
POST /search application/x-www-form-urlencoded:
=> Matched: POST /search (process)
=> Error: The incoming form failed to parse.
=> Outcome: Failure
=> Warning: Responding with 422 Unprocessable Entity catcher.
=> Response succeeded.
If I go directly, to localhost:8000/search/Hello! I can see that my GET response works. But if I use my form it refuses to parse. What am I doing wrong? I am simply attempting to make a web app that takes an input, and based on that input, returns something. Website redirection, web scraping, I am not sure on the specifics of functionality yet, but I need to be able to type something into the form and obtain it for use in my rust code later. Any help would be appreciated!
I think the problem is that your form parameter name (<input type="text" name="searchterm">) doesn't match with your struct field name (payload). If you rename one or the other so they would match, your form should work.

Display Error message after oData Update

I perform an update via my OData service like this:
oModel.create('/Carriers', oEntry, null, function () {
oModel.refresh();
sap.m.MessageBox.show("Saved", sap.m.MesESS);
}, function (err) {
var message = $(err.response.body).find('message').first().text();
sap.m.MessageBox.show(message, sap.m.MessageBox.Icon.ERROR);
});
If I get an error message in the response I am unable to display the message text.
I create the error like this:
CALL METHOD lo_message_container->add_message_text_only
EXPORTING
iv_msg_type = 'E'
iv_msg_text = msg_text.
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
message_container = lo_message_container.
ENDIF.
The err.response.body looks like this:
"{"error":{"code":"SY/530","message":{"lang":"en","value":"This
is the error I am trying to show)"},
"innererror":{"transactionid":"B20B61E5143BF10E92CB000C29D28D3A","timestamp":"20150922092421.4230000","Error_Resolution":{"SAP_Transaction":"Run
transaction /IWFND/ERROR_LOG on SAP NW Gateway hub system and search
for entries with the timestamp above for more details","SAP_Note":"See
SAP Note 1797736 for error analysis
(https://service.sap.com/sap/support/notes/1797736)"},"errordetails":[{"code":"","message":"This
is the error I am trying to show
","propertyref":"","severity":"error","target":""},{"code":"/IWBEP/CX_MGW_BUSI_EXCEPTION","message":"","propertyref":"","severity":"error","target":""}]}}}"
I was trying this but it does not work...
var message = $(err.response.body).find('message').first().text();
sap.m.MessageBox.show(message, sap.m.MessageBox.Icon.ERROR);
It looks like your error is mistaking JSON for a DOM object.
jQuery (the $-sign function) is meant to select DOM elements with the tag name, classes or the id.
To find the right key in an JSON structure, you can use normal JavaScript dot- or brackets-notation:
var message = err.response.body.error.message.value;

How do I save an Angular form to my ruby on rails backend?

I'm new to Angular. I've tried everything I know how and Google searches have surprisingly few tutorials on this particular question. Here's the last code I tried:
index.html
<form ng-submit="addArticle(articles)">
<input type="text" id="title" ng-model="newPost.title">
<input type="text" id="body" ng-model="newPost.body">
<input type="submit" value="Submit">
</form>
articles controller
app.controller('ArticlesCtrl', function($scope, Article) {
$scope.articles = Article.query();
$scope.newPost = Article.save();
});
articles service (rails backend)
app.factory('Article', function($resource) {
return $resource('http://localhost:3000/articles');
});
I can retrieve data just fine. But I can't submit any new data to the rails backend. On page load, the rails server error is:
Started POST "/articles" for 127.0.0.1 at 2015-02-08 18:26:29 -0800
Processing by ArticlesController#create as HTML
Completed 400 Bad Request in 0ms
ActionController::ParameterMissing (param is missing or the value is empty: article):
app/controllers/articles_controller.rb:57:in `article_params'
app/controllers/articles_controller.rb:21:in `create'
Pressing the submit button does nothing at all. The form basically does not work and the page is looking for a submission as soon as it loads.
I understand what the error says, that it's not receiving the parameters from the form. What I don't understand is what that should look like in my controller and/or form.
What am I doing wrong and how do I fix this?
Angular has a feature called services which acts as a model for the application. It's where I'm communicating with my Rails backend:
services/article.js
app.factory('Article', function($resource) {
return $resource('http://localhost:3000/articles/:id', { id: '#id'},
{
'update': { method: 'PUT'}
});
});
Even though the :id is specified on the end, it works just as well for going straight to the /articles path. The id will only be used where provided.
The rest of the work goes into the controller:
controllers/articles.js
app.controller('NewPostCtrl', function($scope, Article) {
$scope.newPost = new Article();
$scope.save = function() {
Article.save({ article: $scope.article }, function() {
// Optional function. Clear html form, redirect or whatever.
});
};
});
Originally, I assumed that the save() function that's made available through $resources was somewhat automatic. It is, but I was using it wrong. The default save() function can take up to four parameters, but only appears to require the data being passed to the database. Here, it knows to send a POST request to my backend.
views/articles/index.html
<form name="form" ng-submit="save()">
<input type="text" id="title" ng-model="article.title">
<input type="text" id="body" ng-model="article.body">
<input type="submit" value="Submit">
</form>
After getting the service setup properly, the rest was easy. In the controller, it's required to create a new instance of the resource (in this case, a new article). I created a new $scope variable that contains the function which invokes the save method I created in the service.
Keep in mind that the methods created in the service can be named whatever you want. The importance of them is the type of HTTP request being sent. This is especially true for any RESTful app, as the route for GET requests is the same as for POST requests.
Below is the first solution I found. Thanks again for the responses. They were helpful in my experiments to learn how this worked!
Original Solution:
I finally fixed it, so I'll post my particular solution. However, I only went this route through lack of information how to execute this through an angular service. Ideally, a service would handle this kind of http request. Also note that when using $resource in services, it comes with a few functions one of which is save(). However, this also didn't work out for me.
Info on $http: https://docs.angularjs.org/api/ng/service/$http
Info on $resource: https://docs.angularjs.org/api/ngResource/service/$resource
Tutorial on Services and Factories (highly useful): http://viralpatel.net/blogs/angularjs-service-factory-tutorial/
articles.js controller
app.controller('FormCtrl', function($scope, $http) {
$scope.addPost = function() {
$scope.article = {
'article': {
'title' : $scope.article.title,
'body' : $scope.article.body
}
};
// Why can't I use Article.save() method from $resource?
$http({
method: 'POST',
url: 'http://localhost:3000/articles',
data: $scope.article
});
};
});
Since Rails is the backend, sending a POST request to the /articles path invokes the #create method. This was a simpler solution for me to understand than what I was trying before.
To understand using services: the $resource gives you access to the save() function. However, I still haven't demystified how to use it in this scenario. I went with $http because it's function was clear.
Sean Hill has a recommendation which is the second time I've seen today. It may be helpful to anyone else wrestling with this issue. If I come across a solution which uses services, I'll update this.
Thank you all for your help.
I've worked a lot with Angular and Rails, and I highly recommend using AngularJS Rails Resource. It makes working with a Rails backend just that much easier.
https://github.com/FineLinePrototyping/angularjs-rails-resource
You will need to specify this module in your app's dependencies and then you'll need to change your factory to look like this:
app.factory('Article', function(railsResourceFactory) {
return railsResourceFactory({url: '/articles', name: 'article');
});
Basically, based on the error that you are getting, what is happening is that your resource is not creating the correct article parameter. AngularJS Rails Resource does that for you, and it also takes care of other Rails-specific behavior.
Additionally, $scope.newPost should not be Article.save(). You should initialize it with a new resource new Article() instead.
Until your input fields are blank, no value is stored in model and you POST empty article object. You can fix it by creating client side validation or set default empty string value on needed fields before save.
First of all you should create new Article object in scope variable then pass newPost by params or access directly $scope.newPost in addArticle fn:
app.controller('ArticlesCtrl', function($scope, Article) {
$scope.articles = Article.query();
$scope.newPost = new Article();
$scope.addArticle = function(newPost) {
if (newPost.title == null) {
newPost.title = '';
}
// or if you have underscore or lodash:
// lodash.defaults(newPost, { title: '' });
Article.save(newPost);
};
});
If you want use CRUD operations you should setup resources like below:
$resource('/articles/:id.json', { id: '#id' }, {
update: {
method: 'PUT'
}
});

Looking for paradigm to use for generic error handling in Angular from a JSON response from Rails

I'm building an app which is architected as a Rails server app providing RESTful api's to the client. The Rails server uses RABL. The client is an Angular JS client performing standard $http calls (gets, puts, etc).
Occasionally my Rails server will produce an error (let's say validation error attached to the object) or even no error in which case I would want to display something to the user - either the errror e.g., "The record did not save because..." or "The record was updated successfully".
I'm trying to map out a pattern on both the Rails side and the Angular/client side to handle this.
As for Rails:
I can certainly pass back a node in each of my RABL files to contain error arrays
I can also return different RABL by checking in the controller before returning
Most suggest using http codes (which makes sense) as per here (although there doesn't seem to be a consistent usages of the codes for something like a validation error).
As for Angular:
I suppose I can write a response interceptor but not sure how that would fully get flushed out.
I guess I'm hoping that I don't have to reinvent the wheel here and someone can point me to a pattern that's currently used and suggested (and localized).
I went ahead and implemented what I thought needed to be done. Thanks for digger69 for some help with this.
On the Rails side, I went with using an http status code. As per here I agreed with using a 400 http status code for error validation.
In my controllers I now have something like the following:
def create
my_obj = MyObj.build_with_params(params)
if my_obj.save
respond_with(my_obj) # regular RABL response
else
respond_with_errors(my_obj.errors)
end
end
In my application_controller.rb I defined a common method respond_with_errors
# respond back to the client an http 400 status plus the errors array
def respond_with_errors(errors)
render :json => {:errors => errors}, :status => :bad_request
end
Note that the :bad_request symbol is already defined for Rails as per here
On the client side I needed to intercept http calls (not only for validation but for authentication failures too (and probably more). Here is an example of my code in Angular (thanks to this post for the help with that):
var interceptor = ['$rootScope', '$q', function (scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) { // unauthorized - redirect to login again
window.location = "/";
} else if (status == 400) { // validation error display errors
alert(JSON.stringify(response.data.errors)); // here really we need to format this but just showing as alert.
} else {
// otherwise reject other status codes
return $q.reject(response);
}
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
I now can be consistent with my rails code and deal with success returns from http calls on the client. I'm sure I have some more to do, but I think this gives a localized solution.
Use an HTTP response interceptor. I am currently using that successfully in an application.
http://docs.angularjs.org/api/ng.$http
From the documentation:
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return function(promise) {
return promise.then(function(response) {
// do something on success
}, function(response) {
// do something on error
if (canRecover(response)) {
return responseOrNewPromise
}
return $q.reject(response);
});
}
});
$httpProvider.responseInterceptors.push('myHttpInterceptor');
In my case I created a feedback service, which displays either success or error messages globally. An other option would be to broadcast the responses on the rootscope.

Resources