Stimulus Discourse

Finding targets within another target


#1

Is the use of querySelector discouraged in favor of always using targets? I’m having trouble understanding how to select groups of targets based on being inside, say, another target.

Given this markup:

<div data-controller="quiz">
  <div data-target="quiz.question">
    <input type="radio" data-target="quiz.answer" data-action="quiz#answer" />
    <input type="radio" data-target="quiz.answer" data-action="quiz#answer" />
  </div>
  <div data-target="quiz.question">
    <input type="radio" data-target="quiz.answer" data-action="quiz#answer" />
    <input type="radio" data-target="quiz.answer" data-action="quiz#answer" />
  </div>
</div>

When triggering quiz#answer, I want to select all quiz.answer targets within the same quiz.question. Is this possible with targets or do I have to use querySelectorAll?


#2

I had a similar case, what I ended up doing was finding the closest question e.target.closest(".question") and then a querySelector. I don’t think the use of querySelector is discouraged when necessary.

Stimulus does rely on querySelectors anyhow -> Are Targets always in document order?


#3

HTML:

<div data-controller="quiz">
  <div data-controller="question" data-target="quiz.question" data-action="answered->quiz#checkQuestion">
    <input type="radio" data-target="question.answer" data-action="question#answer" />
    <input type="radio" data-target="question.answer" data-action="question#answer" />
  </div>
  <div data-controller="question" data-target="quiz.question" data-action="answered->quiz#checkQuestion">
    <input type="radio" data-target="question.answer" data-action="question#answer" />
    <input type="radio" data-target="question.answer" data-action="question#answer" />
  </div>
</div>

quiz_controller.js

import {Controller} from 'stimulus';

export default class Quiz extends Controller {
  static targets = ['question'];
  answered(e){
    let q = this.application.getControllerForElementAndIdentifier(e.currentTarget, 'question');

  }
}

question_controller.js

import {Controller} from 'stimulus';

export default Question extends Controller {
  static targets = ['answer'];
  answer(){
    this.element.dispatchEvent('answered');
  }
}

Maybe try to split your structure to 2 different controllers - quiz and question?

I almost every time end up with similar structure when i need to connect controller instances in parent>child relation. You have 2 controllers - ‘quiz’ and ‘question’. Question dispatch event ‘answered’ to quiz when answer is changed (question#answer). On quiz#answered i used method to find question instance on e.currentTarget (our changed question). I know - this is overkill but i find it useful when you have more complicated HTML structures. Maybe someone from Stimulus creators will figure out how to solve this problem in nicer manner :slight_smile:


#4

Yeah I suppose splitting it into multiple controllers makes sense! Thanks.

I’d be happy to hear what the Stimulus creators think about this as well. As you say, splitting into two controllers might be overkill sometimes.