Ember component not rendering string from API call - ember.js

very new to Ember so I may understand things incorrectly. I have a simple component
UserAdmin.OrganizationStringComponent = Ember.Component.extend({
id: -1,
organizationName: function () {
var id = this.get("id");
var organizationName = "http://localhost:41109/api/org/" + id;
return organizationName
}.property("id")});
The result if this simple \api call is just a string. I copied the idea from a gravatar component that did something similar with an image. Do I have to write a complete ajax call inside the component to make it fire the call?

The short answer is yes
A longer answer, you have a couple options
you can use Ember.RSVP promises to either return a promise, or, after the promise has been fulfilled, return the result
robert ward has a good screencast on how to do this in more detail. definitely worth a look if you're just starting out, as his information is applicable(ember-cli is a set of build tools built around ember)
you can also use Ember.$.ajax
what you're doing right now is setting a variable to a string, then returning that string

Related

Ember.js getJSON data from an url

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.

How do I get the BeforeObserver equivalent in Ember 2.3?

To preface this, I must clarify that I am using the legacy-controller and the legacy-view for the interim period while transitioning to Ember 2.3, found here:
https://github.com/emberjs/ember-legacy-controllers
Now, I have a property called currentTopPost on my (legacy) controller.
In Ember 1.7, I had this setup:
// before observer
currentTopPostBeforeObserver: function(){
...
}.observesBefore('currentTopPost'),
// observer
currentTopPostBeforeObserver: function(){
...
}.observes('currentTopPost'),
The reason I had it this way was that when the currentTopPost changed, I wanted it save the old topPost before it switched its value to the new property, as it was a Post object (I had a Post model).
Of course, in 1.7, I saved the old post in the beforeObserver and then did whatever else I had to do in the observer. Now, In Ember 2.3, I have this set up:
currentTopPostObserver: Ember.observer('currentTopPost', function(){
...
}),
Which works fine as far as performing functions with the new value goes. But I've lost the ability to process an action before the value changes. Now according to an answer to this question:
How can an observer find out the before and after values of the observed property in Ember.js?
the observesBefore function has been deprecated and we should be following this:
doSomething: Ember.observer( 'foo', function() {
var foo = this.get('foo');
var oldFoo = this.get('_oldFoo');
if (foo === oldFoo) { return; }
// Do stuff here
this.set('_oldFoo', foo);
})
However, on trying to use this.get("_oldCurrentTopPost"), I get nothing. How do I access the old value of this property before it changes ?
What I use as a replacement is:
propWillChange(prop) {
//your new before observer
},
propDidChange: Ember.observer('prop', function() {
let prop = this.get('prop');
if (this._oldProp !== prop) {
this.propWillChange(this._oldProp);
this._oldProp = prop;
}
//Do stuff
})
Of course, on the first run, _oldProp will be undefined, but that's expected, right? It is the first time prop is being changed.
I also disagree that observers shouldn't be used. I agree that observers should be avoided if possible, because many people don't fully grasp their side-effects, but in many cases they are very useful, especially when building 3rd party plugin integrations.
Since the question specifically asks about a beforeObserver replacement, here it is. However, I recommend to reconsider if your use case can be rebuilt without observers if possible.
Twiddle: https://ember-twiddle.com/045b7b9c1562ceb6bbdc
As far as I can tell, there is no particularly nice way to get that mechanism back. Observers themselves are "considered harmful" in many cases, but I'll try my best to give you a practical alternative solution.
The best quick and relatively dirty way to do this that I can think of is to make a "proxy" computed property with a getter and setter. Within the setter you can get the previous value of the "real" property, call out to a function to do whatever, and then set the new value on the real property.
Here's an example that you could use:
myProxyProperty: Ember.computed('myRealProperty', {
get() {
return this.get('myRealProperty');
},
set(key, value) {
const oldValue = this.get('myRealProperty');
this.doSomethingWithOldValue(oldValue);
this.set('myRealProperty', oldValue);
}
})
Unfortunately I don't know of a better way of doing this in new Ember at the moment.

EmberJS model hook: this.store.find returns no data. How do I redirect to a 404 page?

When my path /map/:id finds no value via this.store.find('location', route.id), I'd like to redirect to another page instead of receiving an "adapter's response did not have any data" error. It seems to stop processing before it even gets to the controller.
I thought the best way to do this was to extend DS.FixtureAdapter or to return a proxy object until this.store.find resolves. I read the documentation and it said to extend DS.FixtureAdapter via find or findMany hooks, among others. When I tried, none of the events seemed to fire, and I can't figure out an appropriate alternative return object. What am I doing wrong?
this.store.find() returns a promise. Promise resolution has 2 outcomes: 1. good and 2. bad. You can pass in 2 functions into the then() method to tell a promise what to do in each scenario.
So, let's say you are looking for a record and it's not there (bad outcome), you can tell ember to transition to another route.
App.DudeRoute = Ember.Route.extend({
model: function() {
var route = this;
return this.store.find('dude', 5).then(
function(dude){
return dude;
},
function(error){
route.transitionTo('nomansland');
});
}
});
Also note that you need to create a route variable, because just using this inside the bad scenario won't work, since this gets a new context.
Working example here

Detect URL change and grab URL in EmberJS (using Discourse)

I'm using Discourse (http://www.discourse.org/), which is built on EmberJS, and trying to observe any time the URL changes, e.g. when opening a new topic. I've seen the answer for observing the currentPath, for example here:
Detect route transitions in EmberJS 1.0.0-pre.4
App.ApplicationController = Ember.Controller.extend({
routeChanged: function(){
// the currentPath has changed;
}.observes('currentPath');
});
But I'm trying to detect any URL change, not just a path change. As mentioned in the comments for that answer:
This observer doesn't fire when transitioning from for example
/pages/1 to /pages/2 because the path is staying the same:
pages.page.index
What I'd like to do is actually detect those aforementioned transitions which don't get triggered by observes('currentPath'). Along those lines, if I do this.get('currentPath'); inside of my function, I get something like topic.fromParams but I actually am interested in the URL path e.g. /t/this-is-my-url-slug.
To put it simply, I'd like to detect when the app goes from:
/t/this-is-my-url-slug
to
/t/another-url-slug
and be able to capture the path: /t/another-url-slug
Sorry but I'm a bit of an Ember n00b and my only experience with it is through Discourse. Any ideas?
You don't need anything Ember-specific to do this. Depending on whether you are using hash or pushstate, you can use...
$(window).on('hashchange', function(){
console.log("Hash URL is " + location.hash.substr(1));
// Do stuff
});
or
$(window).on('popstate', function(e) {
console.log("Hash URL is " + window.location.pathname);
// Do stuff
});
The solution is pretty specific to Discourse (and not as general to EmberJS), but Discourse has a URL namespace which is called for URL related functions (/components/url.js). There is a routeTo(path) function in there which gets called every time a new route is loaded. So I was able to add my own function inside of there, which ensures that:
my function will be called every time a Discourse route changes
I can capture the path itself (i.e. the URL)
With Luke Melia's answer you are not doing any teardown to prevent memory leaks without causing issues when using the browsers back button.
If this is needed globally for your app, and you only want to use this event to call one function, then ok. But if you want to call off() when you leave the route (which you should tear it down when you don't need it) you will cause bugs with ember. Specifically when trying to use the browsers back button.
A better approach would be to leverage the event bus and proxy the event to one that will not cause issues with the back button.
$(window).on('hashchange', function(){
//Light weight, just a trigger
$(window).trigger('yourCustomEventName');
});
Then When you want to listen to hash changes you listen to your custom event, then tear it down when it is not needed.
Enter Route A:
$(window).on('yourCustomEventName', function(){
// Do the heavy lifting
functionforA();
});
Leave Route A:
$(window).off('yourCustomEventName');
Enter Route B:
$(window).on('yourCustomEventName', function(){
// Do the heavy lifting maybe it's different?
functionforB();
});
Leave Route B:
$(window).off('yourCustomEventName');

Is it possible to hide substates from showing up in the URL when using the Ember Router v2?

I would like to have a route substate not show up in the URL, but still be able to take advantage of having a route class on which I can define renderTemplate, model, setupController, etc. hooks. Is this possible with the v2 router? I am using Ember release candidate 2.
Here's an example.
Suppose I have the routes:
/exercise/:exercise_id
/exercise/:exercise_id/correct
/exercise/:exercise_id/incorrect
I would like all of these to show up in the URL as:
/exercise/:exercise_id
As I don't want the student to just directly type in /correct onto the end of the ULR and get to the correct answer. And although I have a way to prevent that from working, the full route still shows up in the URL. From the student's perspective, I only want them to think about the state as /exercise/:exercise_id.
Of course I could just store the state correct vs. incorrect in some controller variable, but then I loose the convenience of having route classes, ExerciseCorrectRoute and ExerciseIncorrectRoute, which I want to behave differently, and so the hooks, like renderTemplate and setupController, are nice to have defined cleanly in separate places.
Thoughts?
Kevin
UPDATE:
I went with Dan Gebhardt's suggestion because I like to keep things as much as possible within the framework's considered design cases, as this seems to reduce headaches given Ember is still evolving. Also I didn't get a chance to try out inDream's hack.
Although I still think it would be nice if the router added a feature to mask substates from the URL.
Every route must be associated with a URL for Ember's current router.
Instead of using multiple routes, I'd recommend that you use conditionals in your exercise template to call the appropriate {{render}} based on the state of the exercise. In this way you can still maintain separate templates and controllers for each state.
You can reference to my answer in Ember.js - Prevent re-render when switching route.
Reopen the location API you're using and set window.suppressUpdateURL to true if you want to handle the state manually.
Ember.HistoryLocation:
Ember.HistoryLocation.reopen({
onUpdateURL: function(callback) {
var guid = Ember.guidFor(this),
self = this;
Ember.$(window).bind('popstate.ember-location-'+guid, function(e) {
if(window.suppressUpdateURL)return;
// Ignore initial page load popstate event in Chrome
if(!popstateFired) {
popstateFired = true;
if (self.getURL() === self._initialUrl) { return; }
}
callback(self.getURL());
});
}
});
Ember.HashLocation:
Ember.HashLocation.reopen({
onUpdateURL: function(callback) {
var self = this;
var guid = Ember.guidFor(this);
Ember.$(window).bind('hashchange.ember-location-'+guid, function() {
if(window.suppressUpdateURL)return;
Ember.run(function() {
var path = location.hash.substr(1);
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
callback(path);
});
});
}
});