Main question:
Is there something like a willSave or beforeSave or beforeCreateRecordor didCreate method for Ember Data RESTAdapter?
Background:
I have a have some data which requires me to make an extra API call, and use the results of that call, before every createRecord.
The problem is, if I try to override createRecord, the DS.Snapshot therein doesn't allow me to change its properties before it gets saved.
Ideally I'd like to make this call before createRecord but I am open to after createRecord as well.
It also needs to be adapter method as far as I can tell, not a model hook, because there is a native object I need access to which I don't want to save on the server. (i.e. DS.Model's didCreate returns the already-stored data from the server)
I don't think you should count on being able to do something before createRecord since we're encouraged to use it directly if we want to push data onto the store. For example if you want to create a record inside of an action you would do:
this.store.createRecord('bank', {
name: this.get('bankForm.name'),
image: this.get('bankForm.image')
});
You can however override the RESTSerializers serialize method that gets called on save to prepare the object to be sent. Then you can change the data to whatever is appropriate to your use case. http://emberjs.com/api/data/classes/DS.RESTSerializer.html#toc_customizing-an-app-wide-serializer
There's of course an equivalent normalize method to override if you're looking at adding something to the object post-save. http://emberjs.com/api/data/classes/DS.RESTSerializer.html#method_normalize
Related
I am a bit confused. Components, controllers, routes, helpers and whatsoever. I simply want to grab a value from a JSON file and calculate it with a value on Ember.Helper. Which way should i use, i cannot know anymore, brain burned. Would someone please help me to grab the "sell" part of the "market_name" which equals to "BTC_USDT" on "https://stocks.exchange/api2/prices" and put that into helper?
Edited:
In fact i try to do something like that.
import Ember from 'ember';
export function formatBTC(value) {
var url = 'https://stocks.exchange/api2/prices';
var btc_price = Ember.$.getJSON(url).then(function(data) {
for (var i=0; i <= data.length-1; i += 1)
{
if (data[i].market_name == "BTC_USDT")
{
return data[i].sell;
console.log(data[i].sell+' - i got the value properly');
}
}
});
console.log(btc_price+' - shows nothing, i cannot pass the var btc_price to here, why not');
calculation = value * btc_price; //some syntax may apply, maybe Number(value) or whatsoever, but i cannot have my variable btc_price returns here.
return calculation.toFixed(8);
}
export default Ember.Helper.helper(formatBTC);
And from the index.hbs
{{format-btc 0.001}}
Still couldnt find a proper solution. I get the data[i].sell as btc_price, but couldnt pass it through to return part... what am i missing? or what am i doing wrong?
The issue you're encountering is because the ajax request executes. Execution of the function continues and returns the value before the promise returns.
While technically, you could fix this and use async/await in your helper function, you'll run into another issue - Every time your helper is called, you'll make a new ajax request that will fetch the current price and calulate the value.
My recommendation is that instead of a helper, you use a combination of a model and a controller. Because you're currently overwhelmed with the framework, I'll actually make a second suggestion of using a service + component
I recommend a service or a model because you want to persist the data that you've fetched from the pricing source. If you don't, every instance of the helper/component will make a new request to fetch data.
Service
A service is kind of a session collection in ember. It only gets instantiated once, after that data will persist.
ember g service pricing
In the init block, set your default values and make your ajax request.
# services/pricing.js
btcPrice:null,
init() {
this._super(...arguments);
Ember.$.getJSON(...).then(val=>{
# get correct value from response
# The way you were getting the value in your example was incorrect - you're dealing with an array.
# filter through the array and get the correct value first
this.set('btcPrice',val.btcPrice);
})
}
Component
You can then inject the service into the component and use a consistent price.
ember g component format-btc
Modify the controller for the component to inject the service and calculate the new value.
#components/format-btc.js
pricing: Ember.inject.service('pricing')
convertedPrice: Ember.computed('pricing',function(){
return pricing.btcPrice*this.get('bitcoins')
})
The template for the component will simple return the converted price.
#templates/components/format-btc.js
{{convertedPrice}}
And you'll call the component, passing in bitcoins as an argument
{{format-btc bitcoints='1234'}}
All of this is pseudo-code, and is probably not functional. However, you should still be able to take the guidance and piece the information together to get the results you want.
I am trying to implement searching with ember-infinity. But, I do not understand the interaction between the route model and infinityModel.
I have the following code:
model() {
...
return this.infinityModel("myModel", {...}, {...})
}
My search action looks like the following:
search(searchCriteria){
const controller = this.get('controller');
...
_this.infinityModel("myModel", {search:searchCriteria, ...}, {...}).then((myMod)=>{
...
controller.set('model', myModel);
});
}
So this works, but the my query gets fired twice when search is called.
The following only fires the query once.
search(searchCriteria){
const _this = this;
...
_this.infinityModel("myModel", {search:searchCriteria, ...}, {...});
}
But my model does not update. However infinityModelUpdated() function is fired. So I assume that means the infiniteModel was updated, which I assume is my model.
I am pretty sure I am missing something simple. But any help would be greatly appreciated.
Just calling the following:
_this.infinityModel("myModel", {search:searchCriteria, ...}, {...});
does not solve your problem; that is because that method call just returns fresh set of new objects retrieved; which is irrelevant with your original model that you had already returned from model hook. In other words; that method call makes the remote call but does not push the objects retrieved to the model that is already returned from the hook method. If you instead set the model of the controller; then of course the new data is updated to the screen; but I am not sure why a second remote call is being made. That might be related with existence of an infinity-loader already existing in your screen.
What I would suggest is to use updateInfinityModel provided instead of setting the model of the controller. Please take a look at the twiddle I have provided. It uses ember-cli-mirage to mock data returned by the server. Anyway, our point is looking at the makeInfinityRemoteCall action.
this.infinityModel("dummy", { perPage: 12, startingPage: 5}).then((myModel)=>this.updateInfinityModel(myModel));
Here a remote call is made upon button click and data is appended to the model already constructed in model hook. I hope this helps you clear things. Please do not hesitate to alter the twiddle yourself or ask further questions you have.
After your comment, I have updated the twiddle to change the model directly. The duplicate remote call that you have mentioned does not seem to be appearing. Are you sure an exact duplicate remote call is being made? Can it be just the case you are using infinity-loader at your template and a remote call for the next page is being made due to appearance within the view port?
I am returning some static json from my ajax call for test purpose before the bakend is ready. Nut when I use transitionToRoute from some function I can see the model hook of the route is not always called. I guess it is caching the static json and I see the route rendering properly. But I am also setting some other properties of the controller in the setUpController hook which also doesn't get executed when the model hook is not called.
This variable needs to set whenever I am changing to this route. If setUpController is not the place to set it where should I set it . So it doesn't fail to get set when ember doesn't call model hook as part of caching process.
setupController : function(controller, model ) {
controller.set('isEditing',false);
controller.set('messages', model.messages);
controller.set('params', this.get('params'));
console.log('Set Up controller' );
},
model: function( routeParams) {
this.set('params',routeParams);
// return data omitted code
});
}
So the isEdiding field doesn't get set when model hook is bypassed. One get around solution is set it before transitioning like this
this.controllerFor("messages").set('isEditing',false);
// then do tranisitioning
Is there any better way to acheive the same thing? Like ideally where should this variable setting be done if done properly in Ember ?
Sorry I'm late and this might not be of any use for you. I just wanted to post it over here, if in case it might be of any use for others.
This link helped me, clear my problem.
Approach 1:
We could supply a model for the route. The model will be serialized into the URL using the serialize hook of the route:
var model = self.store.find( 'campaign', { fb_id: fb_id } );
self.transitionToRoute( 'campaign', model);
This will work fine for routing, but the URL might be tampered. For this case, we need to add extra logic to serialize the object passed into the new route and to correct the URL.
Approach 2: If a literal is passed (such as a number or a string), it will be treated as an identifier instead. In this case, the model hook of the route will be triggered:
self.transitionToRoute( 'campaign', fb_id);
This would invoke the model() and would correctly display the required URL on routing. setupController() will be invoked immediately after the model().
2nd one worked fine for me fine. Hope it's useful and answered the above question.
Using the latest revision of Ember-Data and the RESTAdapter, is there a way of doing the following?
I have a resource called App and a API that responds to /apps by returning the correct JSON (with { apps: [...] }etc.)
Since this gets served from a static json on our server, it is quiet inappropriate to create server-side resources for every app that can be fetched as /apps/:app_id. Instead, it would be good if the RESTAdapter allways loaded /apps, even if it then only uses one single app out of the fetched ones.
Do I have to write my own Adapter to achieve this? If yes, what would be a good point to "hook into"?
Supposing you have an app model App.App, it should be enough to call App.App.find() when your application loads. This will make the AJAX call to /apps. Even if you don't cache the result in a variable, your data store will be populated with the returned records. Now whenever you call App.App.find(id), Ember Data will check your store and return the record if it has it. If it doesn't have the record, then it will try to call /apps/:id, but this shouldn't happen if your application is designed to use only a static collection.
There are a few different places you could put the App.App.find() call. I would probably put it in App.ready:
App = Ember.Application.create({
ready: function() {
// pre-load apps
App.App.find();
}
});
App.App = DS.Model.extend({
//...
});
It seems a little hacky (and probably is), but it looks like one can achieve this by overwriting the DS.Adapter:find().
In my case, to block calls to /app/:app_id I wrote this:
find: function(store, type, id) {
// Terminate calls for single app
if (type === App.App) {
// instead, load all apps and drop this request
App.App.find();
return;
}
// or continue as usual
this._super(store, type, id);
}
This also works when you have a hierarchy of embedded: 'always' records and Ember thinks it has to load a middle level. Just make sure you load the parent for sure when dropping requests like this!
Somewhere in my ember controllers code i loading some data. It looks like this:
models = App.MyModel.find()
It returns <DS.RecordArray:ember763> and send AJAX. I need to add callback on this data loading.
Something like this doesn't work:
models.on 'didLoad', () ->
console.log 'Loaded' #never triggers
How can i solve this problem?
Thanks.
The issue is that when called with no arguments App.MyModel.find() returns a "live array". Semantically a "live array" is always loaded. Behind the scenes, Ember will query your server the first time find() is called on a model.
Instead, use App.MyModel.find({}) to run a findQuery with no params. See this issue for more detail:
https://github.com/emberjs/data/pull/735