当前位置: 动力学知识库 > 问答 > 编程问答 >

javascript - EmberJS advice on controller, service and component usage

问题描述:

I'm trying to figure out how to properly handle the communication between a controller and several components. I think I need to use a service here, but maybe another solution would me more appropriate.

The idea is that users get served a lesson where one question is given. When a user clicks on the check button, a request to the backend API should be made to check whether an answer is correct. The tricky part is that questions can have different types: open questions, multiple choice and drag and drop.

There is a /lesson/template.hbs which summarized looks like this:

// lesson/template.hbs

<h2>{{model.question.lesson.title}}</h2>

<div class="callout"> {{{model.question.body}}} </div>

{{#if model.question.isMultipleChoice}}

{{answer-multiple-choice-list answers=model.question.answers}}

{{else if model.question.isDragDrop}}

{{answer-drag-drop question=model.question}}

{{else if model.question.isOpen}}

{{answer-open-textarea question=model.question checkAnswer="checkAnswer"}}

{{/if}}

{{#if question_is_answered}}

<a class="next button" {{action 'nextQuestion'}}>Next</a>

{{else}}

<a class="check button" {{action 'checkAnswer'}}>Check</a>

{{/if}}

Then, my idea was to have a lesson/controller.js which handles the both the nextQuestion and checkAnswer click actions:

// lesson/controller.js

export default Ember.Controller.extend({

lesson: Ember.inject.service(),

errorObserver: Ember.on('init', Ember.observer('lesson.has_error', function() {

console.log('Error detected');

})),

actions: {

checkAnswer() {

this.set('lesson.is_check_pressed', true);

}

}

});

And to have a service who is responsible for glueing the controller and corresponding component:

// lesson/service.js

export default Ember.Service.extend({

is_check_pressed: false,

has_error: false,

checkObserver: Ember.observer('is_check_pressed', function() {

console.log('LessonService: Observer: is_check_pressed changed to', this.get('is_check_pressed'));

}),

errorObserver: Ember.on('init', Ember.observer('has_error', function() {

console.log('LessonService: Observer: has_error changed');

})),

});

This service, in the end, will also contain an ajax request to the backend api.

And, for example, the answer-open-textarea component looks as follows:

export default Ember.Component.extend({

lesson: Ember.inject.service(),

given_answer: '',

checkObserver: Ember.on('init', Ember.observer('lesson.is_check_pressed', function() {

console.log('OpenTextAreaComponent, checkObserver');

if( Ember.isBlank( this.get('given_answer') ) )

{

this.set('lesson.is_check_pressed', false);

this.set('lesson.has_error', true);

}

else

{

// Update variable @ service so that the controller knows that the nextQuestion button can be active

}

})),

});

The logic for validating the input, should be in the component, since it does not make sense to have the validity checks for all the possible question types in the controller.

Unfortunately, the code above does not work the way it should, even though this is just the basic idea. What works is, when the check button is pressed, the component checkObserver is fired, as it logs to the console. But it does not trigger the errorObserver in the controller, when updating lesson.has_error in the service. And, even if this would work, both the controller and the component will have several obeservers which I don't feel like to be a good solution.

My question is: how can controllers and components "communicate" in a proper way? By using a service? If yes, then how would that look like? I guess not via Observers.

Thanks in advance!

网友答案:

Services are supposed to be the preferred (and eventually the only) way for controllers and components to directly communicate in the Ember universe. However, I didn't find their use to be immediately intuitive, so I just did what made sense to me;

Ember wants you to follow the 'data-down, actions-up' pattern. What this means is, if you want anything to be in a component, you should pass it down when you instantiate it in the template. If you want a component to affect the controller, you need to pass down an action name, then call sendAction to get the desired effect;

Em.Controller.extend({
  actions: {
    controllerAction: function(){ console.log("you clicked my component"}
  }
})
//template
{{my-component componentAction='controllerAction'}}

Em.Component.extend({
  click: function(){ this.sendAction('componentAction') }

Another way to skin this cat, is to just pass the controller down into the component, and do stuff directly to the controller in the component;

//template 
{{ my-component controller=this }}

This is a cheat, but it works for me. Happy Ember-ing

分享给朋友:
您可能感兴趣的文章:
随机阅读: