Child controller can't retain service attr set in parent controller

Hi all,

read the community guideline while posting but this is a pretty tricky problem:

I’m working with a parent controller and child controller. I’m using a service to track value of an attribute. The parent page loads and after someActionwithQuery, it saves the query in the service The parent page is using a 3rd party JS library (EJ) where the link to child needs to be passed in as a string. So I can’t use LinkTo or TransitionTo.

service:

export default class QueryParamService extends Service {
  // Stores the validated query string so it can be used in multiple controllers/components
  @tracked queryString;

  @action
   someActionwithQuery() {}
  }

parent controller:

export default class parentController extends Controller {
    @service queryParam;

    @action
    saveParam(query){
       this.queryParam.queryString = query;
    }
}

I confirm that the service queryString saves the value

However when I call the service in childController, the value is null.

This problem happens particularly when I use the tag with href= parent/id/child/child_id to directly link to the child route. Also I noticed that this fires both parent and child routes, which is understandable as we’re traversing through the route tree.

Upon investigating, I noticed that the setupController re-initializes the parent and sets up the child controller when clicked on the hyperlink. Could the re-init parent controller be the reason why the service value gets reset?

Here’s my routes:

routes/parent.js:

export default class EnterpriseRoute extends Route {
  model_error = null

  model(params) {
    return this.store.findRecord('parent', params).then((record)=>{
      return record
    })
  }

  setupController(controller, parent) {
     super.setupController(controller, parent);
     window.parent_route = this;
}

routes/parent/child.js:

export default class ParentChildRoute extends Route {

    model(params) {
      return this.store.findRecord('child', params.user_id, {include: 'parent'}).then((record)=> {
        return record
      })
    }

    setupController(controller, child) {
      super.setupController(controller, child);
}

So the question is… how can I use the child route as hyperlink and still retain the service values?

Could you provide more details about what these links look like when rendered? Sounds like when one of these links is clicked it’s actually “fully loading” the app as if it’s another page so the parent route is being re-invoked (it shouldn’t if you’re already in it when you transition) and because the app has been torn down and reinitialized the service state is cleared.

the html code when rendered looks like this:

<a href=/parent/id/child/child_id</a>

EJ embeds this url in a <div> in parent.hbs

You are correct about your assumption on parent route getting re-invoked.

  • The parent route and setup controller fires once (as expected) when I go into the parent page.
  • When I click the url, the parent route/setupController fires again, followed by the child route and setupController
  • When I inspect the controller passed in setupController, I see that all the states defined in parent controller has been reset. That includes the service attributes that was updated in the parent controller.

Is this expected Ember behavior? What surprises me is that the service is also getting reset.

No that’s not how it should be working, my guess is that the way these non-Ember-constructed links are doing a full page load and tearing down/reinitializing the app instead of just transitioning routes. Maybe try doing something similar but with a (hard coded if necessary) link-to and see what happens. Then compare the two links on the rendered page. It’s been a while since I looked at the link-to internals so I kinda forget what all it does…

If I use:

<LinkTo @route="parent.child" @model={{child.id}}>{{child.last_name}}, {{child.first_name}}</LinkTo>

then that works fine. Parent route/setupController doesn’t fire

Problem is I can’t pass in the LinkTo component as a string into the EJ function. EJ doesn’t know what to do with it. Something I thought of is passing something like this:

<a class="someClass" id="someID" href=""></a>

Then I define a jQuery arrow function on-click event and in there I grab the user ID from the event and do a TransitionTo