This question is similar to my previous one: how to communicate with Rails in Ember but some things have changed. Now I know that a kudo is modelled in the server and has the following fields:
value
comment
It belongs to a user. The current implementation is as below:
<a href="#" class="btn btn-primary" {{action "addKudo" user}}>add</a>
Sks.ApplicationRoute = Ember.Route.extend
events:
addKudo: (user) ->
console.log event
self = #
token = $('meta[name="csrf-token"]').attr 'content'
currentUserCon = #controllerFor 'currentUser'
kudosLeft = currentUserCon.get 'kudosLeft'
showFlash = (type, message) ->
$("#flash")
.addClass(type)
.empty()
.append(message)
.show()
.fadeIn()
.delay(2000)
.fadeOut 'slow'
$.post("/kudos", user_id: user.get("id"), authenticity_token: token)
.done((data, status) =>
if kudosLeft > 0
currentUserCon.decrementKudos 1
showFlash 'alert-success', 'You\'ve added a kudo!'
else
showFlash 'alert-error', 'There\'re no kudos left!'
)
.fail (data, status) ->
showFlash 'alert-error', 'Oops! An error occured!'
But now, I'd like to make use of Ember-Data and send a POST request to /kudos (and I'd like to get rid of the jQuery ajax part). Please have a look:
Having the following model:
Sks.Kudo = DS.Model.extend
user: DS.belongsTo 'Sks.User'
value: DS.attr 'number'
comment: DS.attr 'string'
how can I achieve this? What are the neccessary steps to do this? Could anyone shed some light?
The next question is: where select and text area value should point to (bindings)?
Related
i have my website in ruby on rails 5 and i'm updating my stripe payment gateway with this link but clicking my button doesn't redirect me to the stripe checkout, this is what i have in the controller:
def index
Stripe.api_key = Rails.configuration.stripe[:secret_key]
session = Stripe::Checkout::Session.create(
payment_method_types: ['card'],
line_items: [{
price: 'price_1HKywnBS16ZK5Vr3GYCRvTir',
quantity: 1,
}],
mode: 'subscription',
success_url: 'https://www.my_site.network/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://www.my_site.network/cancel',
)
end
I think the error may be when replacing the session id in the javascript code of my index view:
<button id="checkout-button">Pay</button>
<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
var stripe = Stripe('<%= Rails.configuration.stripe[:publishable_key] %>');
var checkoutButton = document.getElementById('checkout-button');
checkoutButton.addEventListener('click', function() {
stripe.redirectToCheckout({
// Make the id field from the Checkout Session creation API response
// available to this file, so you can provide it as argument here
// instead of the {{CHECKOUT_SESSION_ID}} placeholder.
sessionId: '<%=session.id%>'
}).then(function (result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
// using `result.error.message`.
});
});
</script>
you don't have to use '<%=session.id%>'.
The value that you must to use there is the entire value that you store in session when you do: Stripe::Checkout::Session.create in your controller.
I would like to display an error message when the server responses with record not found.
The model in the route handler:
model: function(userLoginToken) {
var userLoginToken= this.store.createRecord('userLoginToken');
return userLoginToken;
},
The action:
actions: {
sendOTP: function(userLoginToken) {
var thisObject = this;
var model=this.currentModel;
this.store.findRecord('user-login-token', userLoginToken.get('mobileNumber')).then(function(response) {
//thisObject.get('controller').set('model', response);
},
function(error) {
//thisObject.get('controller').set('model', error);
//alert("model======== "+model.get('errors'));
});
},
The template is not displaying any error message.
The template:
{{#each model.errors.messages as |message|}}
<div class="errors">
{{message}}
</div>
{{/each}}
Unfortunately, the error message doesn't appear.
Ember depends on an DS.error object, in order to get errors from your models the response has to fulfill the requirements. In order to get Ember to recognize an valid error, in Ember 2.x the error code MUST be 422 and has to follow jsonapi http://jsonapi.org/format/#errors-processing
If you want to catch the errors from the backend response you have to use the catch method:
this.store.findRecord('user-login-token', userLoginToken.get('mobileNumber'))
.then(success => {
// Do whatever you need when the response success
})
.catch(failure => {
// Do whatever you need when the response fails
})
},
For catching the errors automatically as you are doing in your template, your backend needs to response in the right way. I would suggest you to read the answer for this SO question.
I got live updates working with ember and Rails 4 with the help of Railscast #401. ActionController::Live on the backend and EventSource in my Ember code.
The event listener prepends a div with the content sent from the server, but there are 2 problems:
The template on my local browser updates automatically, causing 2 duplicate records to display. So I tried to create a temporary client ID and compare ids before prepending to the DOM. This proved to be quite glitchy, plus it doesn't seem like the ember way...
I found the ember store 'push' and 'pushPayload' methods but I couldn't get those to update my template either.
Here is the relevant code:
Method 1 Using DOM prepend -
Auth.NotebookIndexRoute = Ember.Route.extend(
model: ->
#modelFor('notebook').get('notes')
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.addEventListener 'message', (e) ->
data = $.parseJSON(e.data)
unless self.controllerFor('postsNew').get('savedId') is data.id
$("#allposts").prepend $("<div class=\"post\">").text(data.content)
'savedId' is set in the Posts.new controller after a post is saved. This is hit or miss...
Method 2 Using store.push -
Auth.NotebookIndexRoute = Ember.Route.extend(
model: ->
#modelFor('notebook').get('notes')
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.addEventListener 'message', (e) ->
data = $.parseJSON(e.data)
self.store.push "post",
id: data.id
content: data.content
The push method does not update the template.
Method 3 - Works SOME of the time
Auth.NotebookIndexRoute = Ember.Route.extend(
model: ->
#modelFor('notebook').get('notes')
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.addEventListener 'message', (e) ->
data = $.parseJSON(e.data)
self.store.find("post", data.id).then (stuff) ->
console.log('PUSH NEW POST')
posts = self.modelFor('notebook').get('posts')
posts.addObject(stuff)
When I open up a Chrome and Firefox browser side by side and add new posts, they'll show up only about 60-70% of the time...still looking for where my error might be.
Thanks in advance for any help I can get.
Using Ember-Data, by adding the object that is sent by the server, the application is able to sync to the server and update the template. This seems clean and it works consistently...but only in Chrome, not in Firefox.
I'm adding it as an answer because it's a step forward for this question, but its not the correct answer yet. Hoping to find something soon.
Auth.NotebookIndexRoute = Ember.Route.extend(
activate: ->
self = #
source = new EventSource('/api/v1/testposts')
source.onmessage = (e) ->
console.log('This shows up in Chrome but not in Firefox')
data = $.parseJSON(e.data)
self.store.find("post", data.id).then (stuff) ->
posts = self.modelFor('notebook').get('posts')
posts.addObject(stuff)
stuff.save()
Is there proper way to handle custom error when saving a model? To give an example, lets say I have a model with just two properties "name" and "value". And when I do :
var myModel = this.get('store').createRecord('myModel', {"name": "someName", "value": "someValue"});
myModel.save().then(function() {
//if success
//server responded with {"myModel:{"id":1,"name":"someName","value":"someValue"}"}
},function() {
//if failure
//server responded with {"error":"some custom error message"}
//BUT HOW TO CATCH THIS AND POSSIBLY REMOVE THE MODEL FROM THE STORE
});
One way to work around this is to make extra ajax call to check if the name is unique and then do the save. I am just wondering what is the best/elegant approach here.
Thanks,
Dee
EDIT : I thought it might help a bit to give more context on the server side of the things in groovy. So here it is:
In my controller I have :
def create() {
try {
newRow = someService.create(params)
render someService.list(newRow) as JSON//returns data in format needed by ember-data
}
catch (ValidationException ex) {
def errors = ["errors":[]]
ex.errors.allErrors.each{
if(it.arguments[0] == "fieldName" && it.code=="constrantViolated"){
errors.errors.push(["field":it.arguments[0],"message":"some custom message"])
}
}
//I am using 422 here because of post in http://stackoverflow.com/questions/7996569/can-we-create-custom-http-status-codes
render(status: 422, contentType: 'JSON', text: (errors as JSON))
}
}
Then in my ember controller:
var myModel = self.get('store').createRecord('myModel ', myModelDataInJSON);
myModel .save().then(function () {
//if success
},
function (response) {
myModel .deleteRecord();
var errors = $.parseJSON(response.responseText);
for (var key in errors.errors) {
//do something
}
});
deleteRecord will delete the record.
myModel.save().then(function(response) {
//if success
//server responded with {"myModel:{"id":1,"name":"someName","value":"someValue"}"}
},function(response) {
//if failure
//server responded with {"error":"some custom error message"}
//BUT HOW TO CATCH THIS AND POSSIBLY REMOVE THE MODEL FROM THE STORE
if(response.error=='no good'){
myModel.deleteRecord();
}
});
You can handle errors at model by adding properties into your model:
becameError: ->
# handle error case here
alert 'there was an error!'
becameInvalid: (errors) ->
# record was invalid
alert "Record was invalid because: #{errors}"
Check: How should errors be handled when using the Ember.js Data RESTAdapter?
Why wouldn't the answer be to just use the: DS.ERRORS CLASS?
From EmberJS docs:
For Example, if you had an User model that looked like this:
App.User = DS.Model.extend({
username: attr('string'),
email: attr('string')
});
And you attempted to save a record that did not validate on the backend.
var user = store.createRecord('user', {
username: 'tomster',
email: 'invalidEmail'
});
user.save();
Your backend data store might return a response that looks like this. This response will be used to populate the error object.
{
"errors": {
"username": ["This username is already taken!"],
"email": ["Doesn't look like a valid email."]
}
}
Errors can be displayed to the user by accessing their property name or using the messages property to get an array of all errors.
{{#each errors.messages}}
<div class="error">
{{message}}
</div>
{{/each}}
Is this question only focused on validation into the model? versus persistence/saving it, hence data is clean already before it hits a data store... Seems like you would still want error management at the adapter data store level, too.
That all said, why wouldn't you just use template or normal based JS validation at the UI control level?
I want to get the users Geolocation. Once I get the coordinates, I want to load Records based on that Geolocation. If there is more than 1 result, show an index page,
LHW.Store = DS.Store.extend
revision: 11
adapter: DS.RESTAdapter
DS.RESTAdapter.reopen
namespace: 'api'
LHW.Company = DS.Model.extend
name: DS.attr 'string'
address: DS.attr 'string'
city: DS.attr 'string'
LHW.IndexRoute = Ember.Route.extend
setupController: (controller, route) ->
# get Geolocation
_route = #
navigator.geolocation.getCurrentPosition ((location) ->
#success
_route.geoLocation(location)
), (error) ->
#error
switch(error.code)
when error.PERMISSION_DENIED then console.log "User denied the request for Geolocation."
when error.POSITION_UNAVAILABLE then console.log "Location information is unavailable."
when error.TIMEOUT then console.log "The request to get user location timed out."
when error.UNKNOWN_ERROR then console.log "An unknown error occurred."
geoLocation: (location) ->
_route = #
#get('store').findQuery('company',{longitude: location.coords.longitude, latitude: location.coords.latitude}).then ((data) =>
_route.controllerFor('companies').set('content', data)
if data.get('length') > 1
_route.transitionTo('companies');
), (error) ->
console.log "error"
LHW.CompaniesRoute = Ember.Route.extend()
LHW.IndexController = Ember.ArrayController.extend
beforeModel: ->
console.log 'before'
So this is what I got now. But this has a few problems
If you go to /companies instead of / It skips the geolocation
check. But if I move the geolocation to the companies route, then
what is the page the users sees while geolocation is getting
fetched?
Creating a switch and checking the length in a route,
feels a bit dirty, imo the Companyroute should respond to that
So this is mostly a flow issue I can't wrap my head around and the async geolocation isn't making things easier...