Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The right way to use meteor-semantic-ui's modal? #25

Closed
wuxianliang opened this issue Nov 24, 2014 · 31 comments
Closed

The right way to use meteor-semantic-ui's modal? #25

wuxianliang opened this issue Nov 24, 2014 · 31 comments
Labels

Comments

@wuxianliang
Copy link

When I use modal, I found if I open it with meteor template, I mean, rendering when a session variable is true, there is event binding but transition animation, else if I open modal with jquery $('.ui.modal').modal('show'), there is transition animation but event bindings. So I think there is other ways to use modal which keep functions from Blaze and animations from Semantic-ui at the same time. Dynamic part for integration of these two frameworks is not so straightforward. Would you like to point out the right way to use meteor-semantic-ui's modal? Thank you very much.

@nooitaf
Copy link
Owner

nooitaf commented Nov 25, 2014

I dont know how to answer this correctly, as there are multiple ways to tackle a problem like this. The first thing that popped into my mind was using Tracker.autorun. It watches the Session variable and as soon as it changes it will fire some javascript.

http://docs.meteor.com/#/basic/tracker
http://docs.meteor.com/#/full/tracker

Untested code...

Tracker.autorun(function () {
  if (Session.get("modal_is_open")) {
    $('.ui.modal').modal('show')
  } else {
    $('.ui.modal').modal('hide')
  }
});

You could also try to use multiple modals and talk to them individually..
Untested code..

Tracker.autorun(function () {
  if (Session.get("modal_1_is_open")) {
    $('#modal1').modal('show')
  } else {
    $('#modal1').modal('hide')
  }
  if (Session.get("modal_2_is_open")) {
    $('#modal2').modal('show')
  } else {
    $('#modal2').modal('hide')
  }
});

Or maybe you could avoid using Sessions for opening and closing, i dont know. Totally depends on your project.

@wuxianliang
Copy link
Author

The problem I met is if I use $('.ui.modal').modal('show') to open modal, there is no logic on the modal which meteor template did not render it but jquery did. If I have a form in modal, I will only see the form but can not summit the form. I am sorry that I am not a professional programmer. I just want to write a personal site. But this simple problem frustrated me.

@nooitaf
Copy link
Owner

nooitaf commented Nov 29, 2014

<head>
  <title>semantic-modal-example</title>
</head>

<body>
  {{> hello}}
</body>

<template name="hello">

  <button class="openModal">Click Me</button>
  <p>Form value: {{formValue}}</p>

  <div id="modalView" class="ui modal">
    <div class="content">
      <div class="ui fluid input">
        <input id="modalInputValue" placeholder="form value here" type="text" value="{{formValue}}">
      </div>
    </div>
    <div class="actions">
      <div class="ui button cancel">Cancel</div>
      <div class="ui button ok">OK</div>
    </div>
  </div>
</template>
if (Meteor.isClient) {

  Session.setDefault("formValue", 'something');

  Template.hello.helpers({
    formValue: function () {
      return Session.get("formValue");
    }
  });

  Template.hello.events({
    'click .openModal': function () {
      $('#modalView')
        .modal({
          onDeny    : function(){
            console.log('canceled')
            return false;
          },
          onApprove : function() {
            var modalInputValue = $('#modalInputValue').val();
            Session.set("formValue", modalInputValue);
          }
        })
        .modal('show')
      ;
    }
  });

}

@nooitaf
Copy link
Owner

nooitaf commented Nov 29, 2014

You might want to consider using Approve / Deny Callbacks.
http://semantic-ui.com/modules/modal.html#/examples

@wuxianliang
Copy link
Author

I see. It works! Thank you very very much!

@nooitaf
Copy link
Owner

nooitaf commented Nov 30, 2014

Great :)

@tehfailsafe
Copy link

In the example above, how do I access the context of the template? Say I want to save some data from the modal and I require the _id of the hello template context and there are multiple form elements. Is it really best to set 5 session variables? And then where to I place the actual meteor call method (or with insecure simply add the insert function)?

@nooitaf
Copy link
Owner

nooitaf commented Jan 5, 2015

Instead of Sessions you could use it like this too...

<head>
  <title>semantic-modal-example</title>
</head>

<body>
  {{> carsView}}
</body>

<template name="carsView">
    {{#each cars}}
        {{> carButton}}
    {{/each}}
</template>


<template name="carButton">

    <button class="openModal">{{name}}</button>

  <div class="ui modal">
    <div class="content">
      <div class="ui fluid input">
        <input class="carname{{_id}}" placeholder="car name here" type="text" value="{{name}}">
      </div>
    </div>
    <div class="actions">
      <div class="ui button cancel">Cancel</div>
      <div class="ui button ok">OK</div>
    </div>
  </div>

</template>
Cars = new Mongo.Collection('cars');

if (Meteor.isClient) {

  Template.carsView.helpers({
    cars: function () {
      return Cars.find();
    }
  });

  Template.carButton.events({
    'click .openModal': function (event,template) {
      var self = this;
      template.$('.modal')
        .modal({
          onDeny    : function(){
            console.log('canceled')
            return false;
          },
          onApprove : function() {
            var name = $('.carname'+self._id).val();
            console.log(self._id,name);
            Cars.update({_id:self._id},{$set:{name:name}});
          }
        })
        .modal('show')
      ;
    }
  });

}


if (Meteor.isServer){
  Meteor.startup(function(){
    if (!Cars.findOne()){
      Cars.insert({'name':'dodge'});
      Cars.insert({'name':'fiat'});
      Cars.insert({'name':'mercedes'});
    }
  })
}

@tehfailsafe
Copy link

Ah, var self = this. Totally forgot about that fundamental trick. I worked around it with global variables but that is much nicer, thanks!

@ghost
Copy link

ghost commented Jun 28, 2015

I just found out that Blaze events doesn't work for Semantic UI modal templates.
A workaround is to put the modal content in a separate template.
For the modal buttons the onApprove and onDeny callback can be used.

@joryphillips
Copy link

@sanjo Can you elaborate a little more on how to structure the workaround? Thanks!

@ghost
Copy link

ghost commented Jul 9, 2015

@joryphillips Here is an example of a modal with form, based on my use case.

Node: I use the template extensions package for the hooks instead of onCreated, onRendered (https://github.com/aldeed/meteor-template-extension). This is not necessary.

The modal template

<template name="modal">
  <div class="ui small modal">
    <i class="close icon"></i>
    <div class="header">
      {{header}}
    </div>
    <div class="content">
      {{> modalForm}}
    </div>
    <div class="actions">
      <div class="ui deny button">Cancel</div>
      <div class="ui {{#unless isFilledOut}}disabled{{/unless}} primary approve button">
        {{approveText}}
      </div>
    </div>
  </div>
</template>
var template = Template.modal;

template.hooks({
  created: function () {
    var self = this;

    _.defaults(self.data, {
      header: 'My modal header',
      approveText: 'Submit'
    });

    self.form = new ReactiveVar(null);

    self.getForm = function () {
      return self.form.get();
    };
  },

  rendered: function () {
    var self = this;

    self.form.set(
      Blaze.getView($('form', self.firstNode).get(0)).templateInstance()
    );

    var modal = self.$('.ui.modal');
    modal.modal({
      onApprove: function () {
        self.getForm().submit();
      },
      onHidden: function () {
        Blaze.remove(self.view);
      }
    });
    modal.modal('show');
  }
});

template.helpers({
  isFilledOut() {
    var self = Template.instance();
    var formInstance = self.getForm();

    return formInstance && _.every(formInstance.form, function (field) {
      return !!field.get();
    });
  }
});

The modal content template (in this case the form)

<template name="modalForm">
  <form class="ui form">
    {{!-- TODO: Add form inputs --}}
  </form>
</template>
var template = Template.modalForm;

template.hooks({
  created: function () {
    var self = this;

    self.form = {
      // myInput: new ReactiveVar(null)
    };

    self.submit = function () {
      // TODO: Implement form submit action
    };
  }
});

Service to show modals

Modal = {
  show: function (templateName, data = {}) {
    var template = Template[templateName];
    var parentNode = document.body;

    Blaze.renderWithData(template, data, parentNode);
  }
};

@joryphillips
Copy link

@sanjo thank you very much for the quick response! Just wondering, what event fires the modal in your use case?

@ghost
Copy link

ghost commented Jul 9, 2015

This callback is called by semantic ui when the user clicks the approve button.

onApprove: function () {
  self.getForm().submit();
},

self.getForm().submit() is the submit method that I have defined in the modalForm template.

@nooitaf
Copy link
Owner

nooitaf commented Jul 10, 2015

Just reminding you guys of the new location of this package ;)

@dalgard
Copy link

dalgard commented Jul 10, 2015

I've found a pretty clean solution for using Semantic UI's modals; you can see the code here:
Blaze modals: Semantic UI

A lengthier explanation of the pattern can be found here:
A pattern for incorporating modals (Semantic UI)

(Incidentally, applying the pattern to Foundation modals is even simpler:
Blaze modals: Foundation)

@joryphillips
Copy link

@dalgard That hackpad was very well written and taught me everything I needed to know. Thanks!!

@dalgard
Copy link

dalgard commented Jul 12, 2015

I'm glad :)

@seddonm1
Copy link

seddonm1 commented Oct 2, 2015

@dalgard
Thanks for your code it is very helpful.

I have a couple of questions that I believe you will be able to answer:

  • Do you know how to reproduce the 'closable' behaviour of the modal (allow you to close the modal by clicking on the dimmer) with your pattern? Could we attach another event handler to simulate this behaviour?
  • Do you know how to pass variables to the modal target (appMyActionModel) so that an _id value could be passed and handled in the template's methods. Currently I am using session variables but it feels like a hack.

Thanks

@dalgard
Copy link

dalgard commented Oct 3, 2015

Both your requests are met by the pattern I've outlined. In fact, I consider them central to the pattern.

@seddonm1
Copy link

seddonm1 commented Oct 3, 2015

@dalgard
Thanks. I have just read the documentation on the Blaze.renderWithData method and now understand much better what it is doing and successfully passing data.

@dalgard
Copy link

dalgard commented Oct 3, 2015

These days, I'm using a custom openModal binding with the dalgard:viewmodel package, by the way.

@minafarid
Copy link

@sanjo Great tip! I have been banging my head in the wall for 30 minutes!

@zayau
Copy link

zayau commented Nov 5, 2015

I had problem too. When you have modal template and {{> modalTemplate}} in a parent template and do .modal('show') on onRendered of the parent template. Other jquery initializations on the modal template's onRendered doesn't run second time when you open the modal even in tracker.autorun environment. If someone has a solution to this problem, can share please? thanks.

@Gatux
Copy link

Gatux commented Nov 15, 2015

If you want to use Meteor events with semantic ui modal, try to initiate the modal with "detachable: false". It works for me :)

@talha-asad
Copy link

Yeah but kinda kills all the good stuff :)

This post thread has been really helpful. Thanks alot.

@rijk
Copy link

rijk commented Jan 23, 2016

@sanjo Dude thank you for that great tip of putting the modal content in a separate template!

@nooitaf maybe we want to put this in the package's readme? #25 (comment)
Think it will be helpful to others.

@arist0tl3
Copy link

@dalgard Just wanted to say thanks for that hackpad -- especially the onHidden callback method of blaze removal!

@gVolop
Copy link

gVolop commented Mar 1, 2016

if you wanna to use with meteor-blaze template events inside semantic model, you can separate all modal content to another template, and it worked perfectly.
only modal himself events didn't work.

this didn't work

HTML

<template name="firstModal">
    <div id="firstModal" class="ui wide modal">
        <i class="close icon"></i>
        <input type="button" class="ui button" value="click me">
    </div>
</template>

JS

Template.firstModal.events({
    'click .button': function(event, template){
        alert('click !!!!!!!')
    }
});

but this is perfect!!

HTML

<template name="firstModal">
    <div id="firstModal" class="ui wide modal">
        <i class="close icon"></i>
        {{>firstModalContent}}
    </div>
</template>

<template name="firstModalContent">
    <input type="button" class="ui button" value="click me">
</template>

JS

Template.firstModalContent.events({
    'click .button': function(event, template){
        alert('click !!!!!!!')
    }
});

@frankapimenta
Copy link

just by change I have a modal in a page and sometimes it works fine...
but there are times that I have this error:

Uncaught TypeError: $(...).modal is not a function

any of you have any idea regarding this?

somehow there are problems in loading jquery and semantic ui but this is the only place where I have problems with semantic ui related features.

@lesliedoghouse
Copy link

@dalgard
Hi, I really need a good and clean solution for using Semantic UI Modal with proper event capture.

I read that your solution is very good but too bad both of your link (sample and explanation) are broken now. Do you still have the solution and explanation? Thanks.

Regards,
Lesz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests