1

I keep running into problems with nested components in EmberJS with component initialization. I am trying to use jquery.steps (http://www.jquery-steps.com/) to create a wizard.

I have a wizard component, and I want to have each step as a component which can have actions to change the view for the wizard step. However, after initializing jquery.steps, the view is not changing in the wizard. It seems that the actions are not being detected either (is it because jquery.steps has a property called actions too?)

I expect the value of {{temp}} to change in the view depending on the radio button that was selected. This works if I do not instantiate $().steps in the parent component, but stops working if I do instantiate jquery.steps. What could be going on here? And how do I get it to work? I tried using Ember.run in various ways to execute loadSteps() to no avail.

wizard.js

import Ember from 'ember';

export default Ember.Component.extend({
  temp: null,

  didInsertElement: function () {
    this._super();
    this.loadSteps();
  },
  loadSteps: function () {
    this.$().steps({
      headerTag: "h3",
      bodyTag: "section",
      transitionEffect: "slideLeft",
      autoFocus: true
    });
  },

  actions: {
    addByHandSelected: function () {
      this.set('temp', true);
    },
    setupSheetSelected: function () {
      this.set('temp', false);
      //TODO: show file input
    },
    removeStep: function (step) {
      this.$().steps("remove", step);
    },
    addStep: function (step) {
      this.$().steps("add", step);
    },
  }
});

wizard.hbs

{{creation-method-wizard-step addByHandSelected='addByHandSelected' setupSheetSelected='setupSheetSelected'}}
{{label-size-wizard-step}}

creation-method-wizard-step.js

import Ember from 'ember';

export default Ember.Component.extend({
  tagName:'',
  temp: true,
  actions: {
    addByHandSelected: function () {
      this.set('temp',false);
    },
    setupSheetSelected: function () {
      this.set('temp',true);
    }
  }
});

creation-method-wizard-step.hbs

<h3>Creation Method</h3>
<section>
<div class="radio">
{{#radio-button  value="hand" name='creationMethod' groupValue=creationMethod changed="addByHandSelected"}}Enter By Hand{{/radio-button}}
</div>
    <div class="radio">
      {{#radio-button value="setupSheet" name='creationMethod' groupValue=creationMethod changed="setupSheetSelected"}}Use Setup
          Sheet{{/radio-button}}
    </div>
    Temp is {{temp}}
</section>
Asagohan
  • 583
  • 5
  • 19
  • please provide the `radio-button` component or a working example – melc Aug 04 '15 at 09:12
  • radio button can be found here: https://www.npmjs.com/package/ember-radio-button. You can replace the radio buttons with a normal button and the result will be the same. – Asagohan Aug 04 '15 at 10:38

1 Answers1

1

This occurs due to the changes happening to the dom from jquery steps. As a result it breaks the two way binding. One solution could be to send the action to a parent component that is not modified by jquery steps initialization and handles the two way binding of data. Otherwise you will have to manually update the dom. Also here is a similar issue related to refreshing a component using a library that modified the dom structure .

A rough example of these concepts can be found in the following example,

https://emberjs.jsbin.com/jegizusowo/1/edit?html,js,output

js

App.StepsWizardComponent = Em.Component.extend({
  temp: null,

  didInsertElement: function () {
    this._super();
   var self = this;
    Ember.run.scheduleOnce('afterRender', this, function() {
    self.loadSteps();
  });
  },
  loadSteps: function () {
    this.$(".wizard-step").steps({
      headerTag: "h3",
      bodyTag: "section",
      transitionEffect: "slideLeft",
      autoFocus: true
    });
  },

  actions: {
    addByHandSelected: function () {
      console.log("addbyhand1");
      this.set('temp', true);
    },
    setupSheetSelected: function () {
      console.log("setupsheet1");
      this.set('temp', false);
      //TODO: show file input
    },
    removeStep: function (step) {
      this.$().steps("remove", step);
    },
    addStep: function (step) {
      this.$().steps("add", step);
    },
  }

});

App.CreationMethodWizardStepComponent = Em.Component.extend({
  classNames:["wizard-step"],
//   tagName:'',
  temp: true,
  actions: {
    addByHandSelected: function () {
      console.log("addbyhand2");
      console.log(this.get("temp"));
      this.set('temp',false);
      this.$("#temp-val").text(this.get("temp"));
      this.get("parentView").send("addByHandSelected");
    },
    setupSheetSelected: function () {
      console.log("setupsheet2");
      console.log(this.get("temp"));
      this.set('temp',true);
      this.$("#temp-val").text(this.get("temp"));

 this.get("parentView").send("setupSheetSelected");
    }
  }
});
Community
  • 1
  • 1
melc
  • 11,523
  • 3
  • 36
  • 41
  • Thanks for the detailed answer with nice examples – Asagohan Aug 04 '15 at 23:04
  • How would I handle the binding in a parent component? Would I have to add another component to contain StepsWizardComponent and then get the action from CreationMethodWizardStepComponent to get up to the top component, and then pass the property down from the top component template to CreationMethodWizardStepComponent template somehow? – Asagohan Aug 04 '15 at 23:26
  • @Asagohan i'm glad you found it helpful! Regarding the binding you are asking, a simple answer is yes if the data is related to the parent component. In general my advice is to handle actions and data at the component layer (as in your component hierarchy) that they belong. I think the data down actions up approach should only be applied if an action received at a lower level concerns a higher level component. In that case the higher level component should eventually receive the event and manipulate the data. – melc Aug 05 '15 at 14:22