Blaze Components: How to manage data-context between mixins?

Hi guys,

so far I really love BlazeComponents. Now I just stumbled about a UseCase that is givin me a little headache.

This is what I’d like to achieve:

  • have 1 single MotherComponent (with an own template)
  • the MotherComponent shall own & manage several mixin-instances.
  • each if the mixin-instances shall be bound to a common MixinTemplate, but manage it’s own state.

In order to demo this UseCase I created a little game-code-snippet (see below).
Your goal is to consume more than 2 foods and 2 beers. :wink:

Unfortunatly the code NOT work - it seems like right now each Mixin (FoodCounterMixin & BeerCounterMixin) has it’s data-context and the MotherComponent can NOT access it.

Does anyone have an idea how to get this to work?
@mitar: How would you approach this?

This is the JS (scroll to see it all):

// ===================
// COUNTER Super-Mixin
class CounterSuperMixin extends BlazeComponent {
  // COMMON template for Mixins
  template() { return 'CounterSuperMixinTemplate'; }

  constructor(settings) {
    super();
    var self = this;

    self.title = settings.title;
    self.counter = ReactiveField(0);
  }

  events() {
    return super.events().concat({
      'click': this.onClick,
    });
  }

  onClick(event) {
    var self = this;

    self.counter(self.counter() + 1);
  }
}

// FOOD Counter
class FoodCounterMixin extends CounterSuperMixin {
  constructor() {
    super(
      {title: 'Food'}
    );
  }
}
FoodCounterMixin.register('FoodCounterMixin');

// BEER Counter
class BeerCounterMixin extends CounterSuperMixin {
  constructor() {
    super(
      {title: 'Beer'}
    );
  }
}
BeerCounterMixin.register('BeerCounterMixin');

// ===================
// MOTHER-Component that holds all mixins
//  and has access to its instances 
//  to regulate interactive behaviour
class MotherComponent extends BlazeComponent {
  template() { return 'MotherComponentTemplate'; }

  mixins() {
    return [
      new FoodCounterMixin(),
      new BeerCounterMixin(),
    ];
  }

  success() {
    var self = this;

    if ( self.getMixin(FoodCounterMixin).counter() > 2 
      && self.getMixin(BeerCounterMixin).counter() > 2) {
      return true;
    } else {
      return false;
    }
  }

  globalCounter() {
    var self = this;

    return self.getMixin(FoodCounterMixin).counter() + self.getMixin(BeerCounterMixin).counter();
  }
}
MotherComponent.register('MotherComponent');

HTML (scroll to see it all);

<head>
  <title>blaze components</title>
</head>

<body>
  <p>Home</p>
  {{> MotherComponentTemplate}}
</body>

<template name="MotherComponentTemplate">
  <h1>MotherComponent {{motherValue}}</h1>
  <p>Your aim for the night is to buy more than 2 foods and 2 beers!</p>
  {{> FoodCounterMixin}} <!-- This is where the problem seems to be: the mixins are run in their own data-context -->
  {{> BeerCounterMixin}}
  <p>Global Counter: {{globalCounter}}</p>
  {{#if success}}
    <p>YIHAA, YOU SUCCESSFULLY BOUGHT ALL INGREDIENTS!</p>
  {{/if}}
</template>

<template name="CounterSuperMixinTemplate">
  <h1>{{title}}-Counter</h1>
  <button>click me to count a {{title}}</button>
  <p>{{title}}-counts: {{counter}}</p>
</template>

You never include a {{> FoodCounterMixin}}. This means that you are using a mixin as a component. Then you have in fact used the class twice and created two difference instances of the class, one inside the mixins(), and another when you did {{> FoodCounterMixin}}.

You probably want (and you should rename all Mixins to components, because you are really using them as components, but I am leave it so that it is clearer from your example and HTML.

// ===================
// COUNTER Super-Mixin
class CounterSuperMixin extends BlazeComponent {
  // COMMON template for Mixins
  template() { return 'CounterSuperMixinTemplate'; }

  constructor(settings) {
    super();
    var self = this;

    self.title = settings.title;
    self.counter = ReactiveField(0);
  }

  events() {
    return super.events().concat({
      'click': this.onClick,
    });
  }

  onClick(event) {
    var self = this;

    self.counter(self.counter() + 1);
  }
}

// FOOD Counter
class FoodCounterMixin extends CounterSuperMixin {
  constructor() {
    super(
      {title: 'Food'}
    );
  }
}
FoodCounterMixin.register('FoodCounterMixin');

// BEER Counter
class BeerCounterMixin extends CounterSuperMixin {
  constructor() {
    super(
      {title: 'Beer'}
    );
  }
}
BeerCounterMixin.register('BeerCounterMixin');

// ===================
// MOTHER-Component that holds all mixins
//  and has access to its instances 
//  to regulate interactive behaviour
class MotherComponent extends BlazeComponent {
  template() { return 'MotherComponentTemplate'; }

  success() {
    var self = this;

    if ( self.childComponents(FoodCounterMixin)[0].counter() > 2 
      && self.childComponents(BeerCounterMixin)[0].counter() > 2) {
      return true;
    } else {
      return false;
    }
  }

  globalCounter() {
    var self = this;

    return self.childComponents(FoodCounterMixin)[0].counter() + self.childComponents(BeerCounterMixin)[0].counter();
  }
}
MotherComponent.register('MotherComponent');

Oh yeah!

OK I got the idea, BUT how do I access the other Components from the MotherComponent, p.e. to calculate a total of the clicks (of both food & beer)?

For example like this (does not work):

class MotherComponent extends BlazeComponent {
  template() { return 'MotherComponentTemplate'; }

  globalCounter() {
    var self = this;

    var foodCount = self.getComponent(FoodCounterComponent).counter();
    var beerCount = self.getComponent(FoodCounterComponent).counter();

    return foodCount + beerCount;
  }
}
MotherComponent.register('MotherComponent');

I did make you an example above?

You should use childComponents and parentComponent to traverse the component tree.

@mitar: Thanks a lot for your help!! YOU ROCK!!!

IMO: This should be the official Blaze2!!!

This is the final demo code that shows how to access child components from the mother,
and also how to access parent-data from the child:

JS

// ===================
// COUNTER Super-Mixin
class CounterSuperComponent extends BlazeComponent {
  // COMMON template for Mixins
  template() { return 'CounterSuperComponentTemplate'; }

  constructor(settings) {
    super();
    var self = this;

    self.title = settings.title;
    self.counter = ReactiveField(0);
  }

  events() {
    return super.events().concat({
      'click': this.onClick,
    });
  }

  onClick(event) {
    var self = this;
    log('click');

    self.counter(self.counter() + 1);
  }

  theGlobalCount() {
    var self = this;
    return self.parentComponent().globalCounter();
  }
}

// FOOD Counter
class FoodCounterComponent extends CounterSuperComponent {
  constructor() {
    super(
      {title: 'Food'}
    );
  }
}
FoodCounterComponent.register('FoodCounterComponent');

// BEER Counter
class BeerCounterComponent extends CounterSuperComponent {
  constructor() {
    super(
      {title: 'Beer'}
    );
  }
}
BeerCounterComponent.register('BeerCounterComponent');

// ===================
// MOTHER-Component
//  has access to to other components
//  to regulate interactive behaviour
class MotherComponent extends BlazeComponent {
  constructor() {
    super();
    log('constructor');
  }
  template() { return 'MotherComponentTemplate'; }

  globalCounter() {
    var self = this;

    var beerCount = self.childComponents(BeerCounterComponent)[0].counter();
    var foodCount = self.childComponents(FoodCounterComponent)[0].counter();
    return beerCount + foodCount;
  }
}
MotherComponent.register('MotherComponent');

HTML

<head>
  <title>blaze components</title>
</head>

<body>
  <p>Home</p>
  {{> MotherComponent}}
</body>

<template name="MotherComponentTemplate">
  <h1>MotherComponent</h1>
  <p>Global Counter: {{globalCounter}}</p>
  {{> FoodCounterComponent}}<!-- child components to MotherComponent -->
  {{> BeerCounterComponent}}
</template>

<template name="CounterSuperComponentTemplate">
  <h1>{{title}}-Counter</h1>
  <button>click me to count a {{title}}</button>
  <p>{{title}}-counts: {{counter}}</p>
  <p>GLOBAL-count (fromMotherComponent): {{theGlobalCount}}</p>
</template>