Ember Component: Adding new class not being updated in the DOM


#1

UPDATE: Hey all, sorry I will be closing this thread, this was a bug of my own doing. I was simultaneously updating the search list items in a way that my querySelector was finding ember view elements with different ID’s than what was in the DOM, I’m still looking into the specifics of this, as I still don’t fully understand how this is happening, but currently have a working solution…

Hey all, I’ve run across a rather strange situation I cannot for life of me figure out the solution the for. I apologize in advance if this is javascript / DOM quirk, however I have a feeling this has more to do with how Ember components behave (or refresh) in the DOM after adding a class to an element, as I have never come across this situation before with vanilla js, or can find any other posts about this scenario.

What am I trying to build: I’m building a search component that as a user types in the search query, a search result list populates below the search input. I’m implementing the ability for users to be able to hit the “ArrowDown” key and as they do, it will highlight the selected search result (a seemingly standard implementation for a search component).

The Problem: As the search results populate, I am using querySelectorAll to get all the search result elements (built under hood as ember components), and when the user hits the “ArrowDown” key, I then add the class “active” to the first found element (or second, third, and so on as the user continues to hit the “ArrowDown” key).

Here is where things get a little weird and I’m struggling to understand what is exactly happening under the hood. The “active” class seemingly gets applied to the first search result element, as when I console log the element after adding the “active” class, the console shows the element with the “active” class now added in the class string. However, the “active” class IS NOT being added to the actual element in the DOM. I also have this entire logic running inside the Ember “run” function, hoping this would somehow allow Ember to refresh the component that now has the new class attached. In this instance I’m really struggling to find what I am missing, and why the “active” class is not actually being attached to the element in the DOM.

Please see below the screenshot of the element being console logged with the newly added “active” class.

and a screenshot of the actual element in the DOM, without the active class being added.

Also the code for above scenario is below as follows:

handleManualResultSelect(e) {
    let currentPosition = 0;

    if (e.key === 'ArrowDown') {
      console.log('ArrowDown');
      run(() => {
        // HTML5 Implementation
        // ------------------------
        let searchListItems = document.querySelectorAll('.search-list-item-container');
        let currentSearchListItem = searchListItems[currentPosition];
        currentSearchListItem.classList.add('active');
        console.log(currentSearchListItem);
        console.log(currentSearchListItem.classList);
        currentPosition += 1;
      });
    }
  },

Is there something I am missing about ember components and how to properly add classes to them, when querying those elements in the DOM? I just a little baffled at the moment, and could use any insight I could get. Thanks again for any help in advance here.


#2

Instead of querying and manually updating the DOM directly, I would incorporate an isActive property in the list item component and from your parent list component pass in the item’s active status. Some psuedocode:

// In your List component
if (e.key === 'ArrowDown') {
  this.set('selectedListItemIndex', this.get('selectedListItemIndex') + 1);
}

// In your List template
{{#each listItems as |item, index|}}
    {{list-item-component isActive=(if (eq selectedListItemIndex index))}}
{{/each}}

#3

Hey thanks, this is pretty slick, have not used conditionals in component property definitions before. Will look to incorporate, appreciate the reply!


#4

To be clear, that’s pseudocode; I don’t know if that’s actually going to compile. I was just trying to convey the concept.


#5

Totally, makes sense, thanks.