ractivejs / ractive

Next-generation DOM manipulation
http://ractive.js.org
MIT License
5.94k stars 396 forks source link

How to get the component contents within Javascript when the sub-components are dynamically created? #3409

Open ceremcem opened 2 years ago

ceremcem commented 2 years ago

Description:

I've my reasons to re-invent the radio buttons with <button> elements (like displaying "loading" icon and confirming database writes before displaying a "selected" tick). This is the stripped down version of my radio-buttons component (playground):


Ractive.components['radio-buttons'] = Ractive.extend({
  template: `{{>content}}`,
  oninit: function(){
    setSelection = (selection) => {
      // do something else, like setting button colors
      this.fire('select', selection)
    }
    this.on({
      '*.init': (ctx, instance) => {
        instance.on('click', (ctx2) => {
          setSelection(instance.partials.content[0])
        })
      }
    })
    }
})

Ractive.components['radio-button'] = Ractive.extend({
  template: `<button on-click="click">{{yield}}</button>`
})

new Ractive({
  target: 'body',
  template: `
  <radio-buttons on-select="radioSelected">
    <radio-button>hello</radio-button>
    <radio-button>there</radio-button>
  </radio-buttons>  
  `,
  on: {
    radioSelected: function(ctx, selection){
      alert("selection is: " + selection)
    }
  }
})

However, when I try to create the radio-buttons with an {{#each}} loop, buttons' on-click handler doesn't work as intended: playground:

  <radio-buttons on-select="radioSelected">
    {{#each myArray}}
      <radio-button>{{.}}</radio-button>
    {{/each}}
  </radio-buttons>  

Expected output when we click on [a] button: selection is: a Observed output is: selection is: undefined

How can I make it work when I create the sub-components dynamically? What becomes different when we encapsulate the sub-components into the {{#each}} loop?

Versions affected:

1.4

Platforms affected:

Browser

ceremcem commented 2 years ago

Note that if we don't use instance.partials.content[0] and use instance.get('text'), it works as expected (playground):

  <radio-buttons on-select="radioSelected">
    {{#each myArray}}
      <radio-button text="{{.}}">{{.}}</radio-button>
    {{/each}}
  </radio-buttons>  

So the problem is that we can't get the "content" of a component when it's created by a loop.

evs-chris commented 2 years ago

Since content is a partial, it can be any template. In the case of loop, the content is an interpolator that references .. As template content, there isn't really a value associated with it until it's rendered, so there are two things you could do:

  1. which you've already done in your second comment, is add some sort of value to the radio-button and pull that it from the event. This would also open things up to be more than just text.
  2. pull in the innerText from the element firing the event and use that as the value. This is only a solution if you only want your radios to show their exact value.

I suppose you could also combine those in something like this.