Search code examples
componentsractivejs

Best way to invoke a function in a child component from the parent?


I have a ractive component that needs to performs a specific function, when a user asks it to. I.e., not when the component is being initialised, but some time afterwards.

The ractive docs mention events are used to communicate between components and their parents but only in the context of events bubbling from the child to the parent. In this case, we want the parent to invoke something on the child.

Alternatively, there's ractive.findComponent() but the examples don't seem to use it much for ommunicating from the parent to the child.

I've made the following snippet which uses findComponent() and works (click 'Run Code Snippet' then 'Full Page'). However I'm not sure it's optimal:

var coolWidget = Ractive.extend({
  data: {},
  template: `<span>Your favorite fruit is {{ fruit }}</span>`,
  data: function(){
    return {
      fruit: 'banana',
      doCoolThing: function(){
        var component = this;
        console.log('Going to change fruit...')
        setTimeout(function(){
          component.set('fruit', 'avocado')
        }, 3 * 1000)
      }
    }
  }
})

var ui = new Ractive({
  el: '.some-ui',
  data: { name: 'Mike'},
  components: {
    coolWidget
  },
  template: `
    <h1>Hello {{ name}}</h1>
    <button>Press me</button>
    <coolWidget />
  `,
  oncomplete: function(){
    var widget = this.findComponent('coolWidget')
    document.querySelector('button').addEventListener('click', function(){
      widget.get('doCoolThing')();
    })
  }
})
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<div class="some-ui">

</div>

What's the best way invoke a function in a child component from the parent?


Solution

  • If doCoolThing is a top-level option...

    var coolWidget = Ractive.extend({
      data: {},
      template: `<span>Your favorite fruit is {{ fruit }}</span>`,
      data: function(){
        return {
          fruit: 'banana'
        }
      },
      doCoolThing: function(){
        var component = this;
        console.log('Going to change fruit...')
        setTimeout(function(){
          component.set('fruit', 'avocado')
        }, 3 * 1000)
      }
    })
    

    ...then you can invoke the method like so:

    oncomplete: function(){
      var widget = this.findComponent('coolWidget')
      document.querySelector('button').addEventListener('click', function(){
        widget.doCoolThing();
      })
    }
    

    This doesn't just feel better, it actually has a tangible benefit – doCoolThing sits on the coolWidget prototype, so only gets created once no matter how many widgets are instantiated. There's also no internal mucking around with context binding.