Is there a way to a change in a tracked property fire a function

Instead of thinking about running an imperative function when data changes, think about how you would compute the data you want based on the inputs you have. The tracking system will take care of the rest.

For example, this solves your problem:

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

export default class ArticleComponent extends Component {
  @tracked _cachedArticle;

  // WARNING: don't copy-paste this without reading 
  // the caveats below. I'm including it because its
  // the answer to the question, but I disagree with
  // the premise of the question.
  get article() {
    if (this._cachedArticle && this._cachedArticle.id === this.args.id) {
      return this._cachedArticle;
    }
    this.loadArticle(this.args.id);
    return undefined;
  }

  async loadArticle(id) {
    let response = await fetch(`/articles/${id}.json`);
    let json = await response.json();
    this._cachedArticle = json;
  }
}

// The template can just say something like:
//   {{this.article.title}}

Now, the caveat is that doing this correctly requires a lot more code. There are several problems remaining here:

  1. The component can get destroyed while the fetch is running.
  2. To write a reliable test, you’ll need to hook into the fetch from your tests so they can wait appropriately.
  3. The ID can change while the fetch is running, causing a second fetch to start in parallel and the second fetch can resolve slower than the first one, resulting in wrong data on screen.
  4. The fetch can fail, and when it does we should show a useful error state instead of just breaking.
  5. The fetch can be slow, and while it runs we should show a useful loading state.

All of these are solvable problems, but remembering to solve them all thoroughly every time this comes up is why you really should be using a library for this, instead of sprinkling the logic throughout your components. Good choices of library include the router itself, ember-concurrency, or ember-lifeline.

Please dont tell me to fetch the data in the route, i really don’t want to do that.

I won’t tell you to, but to future readers encountering this thread: see Readers' Questions - "Is it bad to load data in components?"

1 Like