Get glimmer component access to stuff from the parent

I’m adding a component to before-topic-list-body. I need for it to get some data from either the category or the topic (and I’ll add the data I want to which ever serializer makes more sense).

I’ve spent a couple hours on this and have decided that I’m at a loss and there’s a good chance that this is a 2-5 minute problem to provide me with a hint that I need.

I vaguely remember thinking or hearing something about this before, but I’m stuck.

2 Likes

:thinking: hmmm

perhaps this topic has something in it that can help?

Thanks, @Lilly ! Not a bad idea, but I was afraid that currentUser is something that applies to the whole page, and not just where the component is being inserted, and I think I’m right. To see what was available that way, I grepped the offical plugins and got this list of services (if I were smart I’d know how to find them in the discourse source, but I’m not):

  @service adminPluginNavManager;
  @service appEvents;
  @service capabilities;
  @service chatApi;
  @service composer;
      @service currentUser;
  @service currentUser;
  @service dialog;
  @service dTemplatesModal;
  @service encryptWidgetStore;
  @service hCaptchaService;
  @service imageCaptionPopup;
  @service menu;
  @service messageBus;
  @service modal;
  @service moreTopicsPreferenceTracking;
  @service presence;
  @service quickSearch;
  @service router;
  @service search;
  @service searchPreferencesManager;
  @service session;
  @service site;
  @service siteSettings;
  @service store;
  @service taskActions;
  @service toasts;
  @service upgradeStore;
  @service userFieldValidations;
  @service whosOnline;
2 Likes

I found it. Here’s where to get it from my context:

<RatingOne @scaleValue={{this.scaleValue}} @topic={{this.parentView._parentView.topic}} />

So I’m able to use that parentView and _parentView to work my way back up to the topic! That’s the magic I’m looking for, (which I think will also solve another issue on another project). Now all I need to do is put the stuff in the topic controller, which is all hopefully stuff that I (at least mostly) know how to do!

1 Like

Did this print a deprecation message to the console? It should’ve done, because we’ll be removing parentView access from PluginOutlet’s imminently. In fact, it’s so un-recommended that it should cause a warning banner to be shown to admins in the UI :sweat_smile:

If you didn’t see any warnings, please let us know!

(parentView is part of Ember’s classic component system, which we’re moving away from)

If you want to access information from higher up in the tree, then it either needs to be passed as an argument to the Plugin Outlet, or you need to use one of the available services (like currentUser, or router)

2 Likes

Ah, I see what’s happened. You’re using ._parentView, not .parentView. The underscored version allows you to bypass the deprecation message. But still, it will stop working within the next few weeks.

This PR prevents that kind of workaround, so .parentView and ._parentView will both raise the deprecation:

2 Likes

Thanks, @david !

No. Not even after I looked for a while. I’m on 27eea3250cbde2e8bca754f445bee403c059eba3

<RatingOne @scaleValue={{this.scaleValue}} @topic={{this.parentView._parentView.topic}} />
<td class="rating-one">
  <span class="rating-title">{{this.ratingName}}</span> 
  This is topic {{this.topic.id}}
  This is category {{this.topic.category.title}} (category id: {{this.topic.category_id}})
  {{log "rating this" this}}
  {{log "scale value" this.scaleValue}}
  {{log "rating value" this.ratingValue}}
  {{log "category" this.topic.category_id}}
    <form>
        {{!-- {{this.ratingLow}} --}}
       {{#each this.ratingOptions as |option|}}
            <input type="radio" name="rating" value={{option.value}} checked={{if (eq option.value this.ratingValue) "checked"}}> <span class="rotated-label">{{option.label}}</span>
        {{/each}}
        {{!-- {{this.ratingHigh}} --}}
    </form>
</td>

Ah. OK. So can I use router to get to the topic or category? What I (think I) need to do is get those ratingOptions into the topic or category serializer so that I can add these multiple topic ratings to the topic-list so that these things can be rated without entering the topics (ostensibly people will already be familiar with them, so they won’t need to enter the topic to know what they are voting on).

Hmm. It doesn’t look like it.

2 Likes

Ok, that should be fixed by this commit. Can you confirm you see the warning now?

Ideally, those kind of things would be available as arguments on the plugin outlet.

You mentioned ‘before-topic-list-body’ above. That has all these arguments:

2 Likes
{{log "rating this parent" this.parentView._parentView.topic}}

generates an ugly error.

<RatingOne @scaleValue={{this.scaleValue}} @passedRouter={{this.router}} @topic={{this.parentView}} />

seems not to generate an error.

I really need above-topic-list-item, but thanks to your fine example, I was able to find that it should have the topic in outletArgs,

Ha! I’m going to do it!

So outletArgs means that it’s now in the this of the thing? (That’s what it looks like–I thought I would need to look in args somehow. . .)

So in my connector hbs, I can access this.topic and then I can call my component like this:

<RatingOne @name="one" @topic={{this.topic}}/>

And then in the hbs for the RatingOne component (one day to be renamed just rating since I’ve figured out how to pass stuff to it) I can

  This is topic {{this.topic.id}} {{this.topic.title}}
  This is category id: {{this.topic.category_id}})

and get the topic stuff!

And now I see the topic, now I can go add my args to the topic serializer, right? (It might be better to pass it just to to the category . . . or maybe I’ll just pass a “doTheThing” value in the serializer and get the actual stuff from SiteSettings, as I think they want this at the site and not category level).

Unless I’ve somehow stumbled on something else deprecated, it seems like you’ve done it. Thanks a zillion. :beer: :beers: :clinking_glasses: :moneybag:

3 Likes

This is related to "how I make stuff work in glimmer, so I’m keeping this here.

I need to call this code after each topic is rendered to re-order the <td> items. I used above-topic-item, which puts the ratings before the topic title. This wild hack re-orders the <td> items the the help of some javascript.

// document.addEventListener("DOMContentLoaded", function() {
//   const table = document.querySelector('table.topic-list.ember-view');
//   const rows = table.getElementsByTagName('tr');

//   // todo: this needs to get called on page load 
//   for (let row of rows) {
//     const cells = Array.from(row.getElementsByTagName('td'));
//     cells.sort((a, b) => {
//       const orderA = parseInt(window.getComputedStyle(a).order, 10);
//       const orderB = parseInt(window.getComputedStyle(b).order, 10);
//       return orderA - orderB;    });

//     for (let cell of cells) {
//       row.appendChild(cell);
//     }
//   }
// });

I think I “just” need to call some afterRender or something else instead of the document-level thing that I have. Maybe the router?

1 Like