5

I'm trying to make a progress bar that gets updated as more challenges are completed. However, the component cannot access the property because this is undefined.

I have injected a service and I'm trying to create a computed property from a property of the service. However, this is always undefined unless in debugging.

import Ember from 'ember'; export default Ember.Component.extend({ progress: 0, game: Ember.inject.service(), events: this.get("game.challenges") }); 

How can this be undefined in the above code? How is it not bound to any scope?

I've thrown in a debugger like this:

init() { debugger }, 

If I log out this.get("game") it returns the expected value.

I've also tried putting in the service inside of the init, still undefined. I've tried logging out this and that is undefined as well.

Is there a way to force the service to resolve before moving on? I have tried using various component hooks but they don't seem to have changed things.

1

2 Answers 2

6

Elaborating on Tom's answer:

In JS, this is kind of a special variable. Its meaning depends on whether it is inside a function or not.

  • Outside a function, it is the global context (in a browser, that's usually the window object).
  • Inside a function, it is the object the function is called on. Eg if you write obj.f(), then this will be obj in f(). If calling the function directly, this remains whatever it currently is.

In your code, the JS engine is currently executing outside a function (it is preparing to call extend, passing it an object).

export default Ember.Component.extend({ game: Ember.inject.service(), events: this.get("game.challenges") }); 

Therefore, this in your code refers to the window object. You fix that using a computed property, that is, a function that gets invoked on your object when the property is accessed:

export default Ember.Component.extend({ game: Ember.inject.service(), events: Ember.computed('game.challenges', function() { return this.get("game.challenges"); }) }); 

You may do whatever computation you need in the function. Just remember than anything the result depends on must be listed inside property() so Ember knows when to recompute it.

Lastly, for some common cases, ember provides some shortcuts, such as Ember.computed.alias, Ember.computed.equal, …

Sign up to request clarification or add additional context in comments.

1 Comment

The way I have tried to understand this before is that it is based upon how the function has been invoked. The problem I think I had, was because it was in a framework, I wasn't sure if there were other things at play. I also thought it would be found to the component because I'm not sure how Ember works behind the scenes setting that up. Your explanation makes a lot sense though and I have a better idea what's going on.
2

You have to tell Ember that this is a computed property. Computed properties are very well documented in the docs -

http://guides.emberjs.com/v2.0.0/object-model/computed-properties/

If you just want it to be the same value from the service, you could alias it like so:

game: Ember.computed.alias('game.challenges')

6 Comments

The problem isn't at this point the computed property, it's that this isn't defined at all. If I try to create a computed property based on an attribute of the service, it fails because it gets a type error on this.
I might have to just make it an alias. I wanted originally to convert the number to a percentage but that just might not be possible.
this is out of context if you just throw it out there the way you currently have it, so of course it's undefined. Wrap it in your own function or in an Ember.computed function and it will correctly reference your component.
Ok, I guess it just doesn't make sense why you are unable to have properties of a component that descend from a service. I don't want them actionable. I want the value of the bar to be based upon something that happened somewhere else. I'm glad at least I know this now. Perhaps I'll just move the code to the service and then just alias the value as you've shown. Thanks for your help.
this in javascript will refer to the outer context, so the way you have it, there isn't an outer context because this is a module living on it's own. That's why when you wrap it in a function, the outer context will then become the Ember.Component. Here's a simplified example jsbin.com/xagoyemanu/edit?js,console
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.