peerlibrary / meteor-blaze-components

Reusable components for Blaze
http://components.meteorapp.com/
BSD 3-Clause "New" or "Revised" License
354 stars 26 forks source link

How do you guys do Modals? #137

Closed thebarty closed 8 years ago

thebarty commented 8 years ago

Hi guys,

I am struggeling with getting Modals to work. I have tried both anti:modals (https://atmospherejs.com/anti/modals) and peppelg:bootstrap-3-modal (https://github.com/PeppeL-G/bootstrap-3-modal) BUT they BOTH simply render the html without assigning the BlazeComponent.js.

Does anyone have a good solution for this?

mitar commented 8 years ago

I just have my own blaze components-based modal.

thebarty commented 8 years ago

Can you share some code? How do you render and destroy it in the dom?

mitar commented 8 years ago

Hm, I do not have an example handy. But I have an top-level modal directly under <body>. And then I have a singleton component rendered there. And then I simply access that singleton component and call methods on it to change state, for example, change visibility state and so on.

thebarty commented 8 years ago

OH yeah I see.. how do you then dynamically pass and render a BlazeComponent-Instance with a given datacontext??

mitar commented 8 years ago

I do not use data context at the top level in that case, but have a reactive var (or reactive field) in onCreated attached to the component. Something like:

globalModalWrapper = null

class ModalWrapper extends BlazeComponents
  @register 'ModalWrapper'

  onCreated: ->
    super

    @dataContext = new ReactiveField null

    throw new Error "Only one modal wrapper can exist at a time." if globalModalWrapper
    globalModalWrapper = @

  onCreated: ->
    super

    globalModalWrapper = null
<template name="ModalWrapper">
  <div class="modal-wrapper">
    {{#if dataContext}}
      {{> ModalContent dataContext}}
    {{/if}}
  </div>
</template>
globalModalWrapper.dataContext({type: 'warning', message: "Something looks bad."});
thebarty commented 8 years ago

Wuhuuuuu - thanks to you @mitar I have gotten close!!!

By the way: I love the idea of having a directory of BlazeComponent-compatible packages. When you take over Blaze for mdg (do you??? 👍 ) we should have that... and a Modal Toolkit should be included...

this is what I have... so far it works

// Variable to access the instance of the modal globally
export let ModalComponentInstance = null

export const UiModalComponent = class UiModalComponent extends BlazeComponent {
  onCreated() {
    super.onCreated()
    this.componentClass = new ReactiveField()
    this.dataContext = new ReactiveField()

    // Singleton pattern
    if (ModalComponentInstance) {
      throw new Error('Only one modal can exist at a time')
    } else {
      ModalComponentInstance = this
    }
  }

  show(component, dataContext) {
    this.componentClass(component)
    this.dataContext(dataContext)
    this.$('#the-modal-singleton').modal('show')
  }

  hide() {
    this.componentClass(null)
    this.dataContext(null)
    this.$('#the-modal-singleton').modal('hide')
  }

  renderedComponent() {
    if (this.componentClass()) {
      return this.componentClass().renderComponent(this.currentComponent())
    } else {
      return null
    }
  }
}
UiModalComponent.register('UiModalComponent')

Bootstrap3 compatible template

<template name="UiModalComponent">
  <div id="the-modal-singleton" class="modal fade">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-body">
          {{#with dataContext}}
            {{>renderedComponent}}
          {{/with}}
        </div>
      </div>
    </div>
  </div>
</template>

put this somewhere in your layout.html

{{>UiModalComponent}}

and then finally you can access the modal programtically from anywhere in your code, and tell it what Component to render with what data-context.

import { ModalComponentInstance } from '/imports/modules/components/client/component_modal.js'

// ...

  onClickEditOpenModal(event) {
    ModalComponentInstance.show(TestComponent, {
      // data context
      addNumber: _.random(1, 100),
    })
  }
mitar commented 8 years ago

This looks great!

thebarty commented 8 years ago

For anyone else working on this: the cool thing with the above solution is that you can still use "theduke:bootstrap-modal-prompt" for standard-prompts within the UiModalComponent. So you basically have another modal-prompt-layer on top of the modal.