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?
Related
Using version 2.17. I have an Ember component inside an /edit route with a controller:
// edit.hbs
{{ingredient-table recipe=model ingredients=model.ingredients}}
Inside my component, I am using a didRecieveAttrs hook to loop through ingredients on render, create proxy objects based off of each, and then build an ingredient table using those proxy objects.
// ingredient-table.js
didReceiveAttrs() {
let uniqueIngredients = {};
this.get('ingredients').forEach((ingredient) => {
// do some stuff
})
this.set('recipeIngredients', Object.values(uniqueIngredients));
}
I also have a delete action, which I invoke when a user wishes to delete a row in the ingredient table. My delete action looks like this:
// ingredient-table.js
deleteIngredient(ingredient) {
ingredient.deleteRecord();
ingredient.save().then(() => {
// yay! deleted!
})
}
Everything mentioned above is working fine. The problem is that the deleted ingredient row remains in the table until the page refreshes. It should disappear immediately after the user deletes it, without page refresh. I need to trigger the didReceiveAttrs hook again. If I manually call that hook, all my problems are solved. But I don't think I should be manually calling it.
Based on the docs, it is my understanding that this hook will fire again on page load, and on re-renders (not initiated internally). I'm having some trouble figuring out what this means, I guess. Here's what I've tried:
1) calling ingredients.reload() in the promise handler of my save in ingredient-table.js (I also tried recipe.reload() here).
2) creating a controller function that calls model.ingredients.reload(), and passing that through to my component, then calling it in the promise handler. (I also tried model.reload() here).
Neither worked. Am I even using the right hook?
I suppose recipeIngredients is the items listed in the table. If that is the case; please remove the code within didReceiveAttrs hook and make recipeIngredients a computed property within the component. Let the code talk:
// ingredient-table.js
recipeIngredients: Ember.computed('ingredients.[]', function() {
let uniqueIngredients = {};
this.get('ingredients').forEach((ingredient) => {
// do some stuff
})
return Object.values(uniqueIngredients)
})
My guess is didReceiveAttrs hook is not triggered again; because the array ingredients passed to the component is not changed; so attrs are not changed. By the way; do your best to rely on Ember's computed properties whenever possible; they are in the hearth of Ember design.
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.
Ok, so here's what I'm trying to accomplish (in ember.js):
New model/controller to manage the current user and session information
The model needs to be available everywhere so I can just do something like currentUser.firstname (for instance, in the nav)
After lots and lots of research, it seems that setting up a separate controller/model is the best way to go. I tried doing everything in the application controller, but then I need to implicitly set the user model (somehow?) on that controller, which doesn't seem like a good idea (what if I need to do other things in the application controller?).
So here's what I've tried:
controllers/session.js
init: function() {
// this never gets called unless I call the setCurrentUser
// function from another controller using 'needs' or something
},
setCurrentUser: function() {
// you can ignore the authData.uid variable - it's something used
// by firebase, but not important for this example. Just assume I'm
// requesting and getting a user back.
this.store.find('user').then(function(users) {
this.set('currentUser', users.filterBy('uid', authData.uid)[0])
}
}
models/session.js
DS.Model.extend({
currentUser: DS.belongsTo('user')
});
I don't have a route or view/template associated with this model/controller because it really wouldn't make sense (the user doesn't need to see a page all about him/herself).
So I tried calling setCurrentUser from the application controller by doing something like this.get('setCurrentUser')() (which looks very weird - there's got to be a better way, but I think the answer may be not calling this from the application controller and initializing the controller in a different way?).
I would love some advice on how I can get this working. Sorry that I'm trying to develop and explain the architecture of the app all at the same time, so it is a bit messy - please let me know if anything is unclear.
Thanks for the help!
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.
I'm loading data from XboxLive and other services in my ApplicationRoute and want to display that information using named outlets. I used this StackOverflow answer as a guide, but it's not working.
Fiddle is here: http://jsfiddle.net/sandalsoft/7xHfp/
In the ApplicationRoute, the model hook loads data from online services. This data is accessible as model.xbox, model.facebook, etc... setupController then sets the content of the XboxprofileController to model.xbox.
This doesn't work when I call App.Xboxprofile.find() (returns a promise), but it does work when I call App.Xboxprofile.findSimple() (returns a simple object). Why doesn't this work when find() has any kind of asynchrony? Am I missing something simple, or is this not the right way to architect this?
thanks
Thanks to #alexspeller in #emberjs for the answer.
I mistakenly thought model.xbox was a promise. It's an object. When I changed my model hook to use RSVP.hash() it worked perfectly:
model: function(controller)
var allModels = Em.RSVP.hash({
xbox: App.Xboxprofile.find('major nelson'),
}).then(function(hash) {
return Em.RSVP.hash(hash);
});
return allModels;
},