Struggling getting rid of observers


#1

I’ve read and watched all of the stuff about “observers are bad”. I get it. But I’m having a heck of time wrapping my head around how to get rid of them (I’m slow, sorry).

For example: I have a component that wraps a bootstrap modal. In the spirit of DDAU, my component has an attribute that the parent can set to true or false as to whether this modal should be open or not.

import Ember from 'ember';

export default Ember.Component.extend({

  openModal: false,

  id: "r-modal",

  didRender: function() {
    this.$('#'+this.get('id')).on('hidden.bs.modal', function() {
      this.set('openModal', false);
      this.sendAction('modalClosed');
    }.bind(this));

    this.$('#'+this.get('id')).on('shown.bs.modal', function() {
      this.sendAction('modalOpened');
    }.bind(this));
  },

  setupModalListener: function() {
    if(this.get('openModal')) {
      this.$('#'+this.get('id')).modal('show');
    } else {
      this.$('#'+this.get('id')).modal('hide');
    }
  }.observes('openModal'),
});

How the heck am I supposed to refactor setupModalListener? If I’m only supposed to send data down to the component, then how does one react to it being ‘true’ if not in an observer? I don’t see an obvious way to send actions down either but that’s really what I semantically want to do here. I want the parent to say “hey modal, open up” (and the reverse). I’d be happy to call a function from parent to child or issue some sort of action from parent to child, but there doesn’t appear to be an obvious mechanism for doing so.

This is literally a case of: I need to call .modal(‘show’) / hide when attribute openModal changes and I don’t see how to accomplish that without observes…

Help my thick skull grok this :slight_smile:


#2

I mean, generally you should not use bootstrap modal dialogs with ember. Ember does not work well with libraries that wrap ember dom, such as modals.

There are many great modal libraries that are ember-native such as https://github.com/yapplabs/ember-modal-dialog

If you really need to do manual dom manipulation, observers are the right place to do it. But not using the bootstrap js is what I strongly recommend in this case.


#3

Ok, so I’m NOT missing some other way to accomplish this sort of pattern? Cool, I was worried I was missing something. I’ll take a look at that add on, thanks.


#4

Using the didReceiveAttrs lifecycle hook would help to get rid of them. But really I agree with @alexspeller that you’re better off using ember-modal-dialog. It’s a great addon.


#5

I’m very likely to adopt ember-modal-dialog as recommended. I took a look at it and like what I see.

That said, I’m still curious about the concept of this pattern. At first read I didn’t understand how didReceiveAttrs would help… then I re-read the API docs on it and feel dumb… for some reason I had it in my head that didRecieveAttrs was only invoked on initial passing of component attributes. Now that I re-read it not only does it say it also fires whenever those attrs change, but it specifically states this as a way of eliminating observers… RTFM I guess. Somehow I missed that umpteen times I’d read it.

THANK YOU!


#6

Thing is most external addons break out of the Ember paradigm, because they use an event hook / action trigger model. Here instead of propagating an isOpen state, you must trigger open and close actions. And should you want to know whether it is open or not, you have to track manually which actions you last triggered. That’s a lot of redundancy in the code, and that’s what Ember wants to avoid.

But yeah, that means that if you really want to interface with such external libs, you must then artificially recreate the missing events, and the way to do so is didReceiveAttrs or observers.


#7

Observers aren’t inherently evil, I think the messaging around this has been overdone a touch. If you can use computed properties then it is (nearly) always better to do that because they’re lazy rather than eager but the core Ember code uses observers a lot and as long as you don’t chuck them around like sweets at a kid’s party then you should be fine.

As others have said, there is often an “Ember way” to accomplish what a traditional plugin can do but I’ve had a couple of times, notably with WYSIWYG editors where I’ve had to go off-piste and use observers and haven’t had any issues thus far :slight_smile:


#8

You may choose to not use bootstrap’s JavaScript at all and only use all the CSS. Roll everything that bootstrap JS does yourself and you’ll have a much easier time.

Now, specially to the modal window itself, I don’t like the design that you give the modal component itself the power to control when it get rendered. If you make the component dumber, things may become more flexible.

For example…

{{! instead of doing this }}
{{#bs-modal visible=propOnController}}
{{/bs-modal}}

{{! do this }}
{{#if propOnController}}
  {{#bs-modal}}
  {{/bs-modal}}
{{/if}}

Make bs-modal dumb. Don’t use any bootstrap’s JS. Render bootstrap’s CSS with Ember template.