Most Useful Meteor APIs

I completely unintentionally discovered an extremely useful function call this morning. I figured we could make a topic where we can share the most useful commands we have learned (probably through extensive googling) and hopefully save someone the trouble in the future.

Quick ground rules:

  1. Try to stay within the Meteor core (as far as the actual useful APIs are concerned). Feel free to link to things where the function calls are used, but try to talk mostly about the Meteor core.
  2. Provide a good description of the function or API and why it is/was useful for you.
  3. If you can provide a link to the actual API documentation, that’d be nice.

I’ll go ahead and start off with the one I discovered this morning, Blaze.getData. This function call lets you pass a DOM element as an argument and it will return the Blaze context that generated that DOM element if it was created from a Template.

What this essentially lets you do is recapture the context of an element when you have events that cannot be bound by the standard Template.events method. For example, I have written a right-click context menu library that, until now, had no knowledge of the Blaze context. However, this morning I added a third parameter to a menu selection’s “action” function that is the Blaze context, which should allow for much better control over the behavior of the right click menu as far as specific behavior per item clicked. You can see the results of this live at contextmenu.meteor.com (the source code is at the repo linked above).

I can’t wait to learn more useful commands from you! Happy coding!

18 Likes

Excellent!! This thread could be really useful for community.

Super simple one, but handy to know about if you’re brand new:

Triple brackets = raw HTML insert.
So, if you’re in a template and you have something like this:

<div>
  {{{rawContent}}}
</div>

where your template.[myTemplate].helpers looks like this:

...
rawContent: function(){
  ...  
  return rawC; // returns "<div>Hi, I'm raw content</div>"
}
...

The triple stash turns it into actual HTML instead of a string interpretation.

It’s not in the official documentation that I know of, but here’s a couple of links explaining it:
Stack Overflow Question
meteor-talk (David Greenspan explanation)

2 Likes
Meteor.defer(function(){
  // do stuff later
});

This is essentially a more efficient implementation of Meteor.setTimeout with 0 ms. @arunoda wrote a good blog post about this here. It’s actually a super useful and worth looking into.

8 Likes

Along the same lines of the triple brackets, Spacebars.SafeString will perform a similar function. If you had the following code, it would perform the same:

<div>
   {{rawContent}}
</div>

where the template.[myTemplate].helpers looks like:

rawContent: function() {
    var html = new Spacebars.SafeString("<div>Hi, I'm raw content</div>");
    return html;
}

A cautionary tale about using the triple brackets or the Spacebars.SafeString. Make sure that you are not trusting user input and injecting the raw HTML into the page. This could potentially create a situation where cross-site scripting becomes an issue. Make sure you are properly escaping any data that the user is giving if you are adding it to the page. Just a heads up. Note that double brackets {{}} does this by default. You only need to sanitize user input if you are explicitly injecting raw HTML into the page.

4 Likes

Great idea (mdg should collect these suggestions to improve the documentation) !

My contribution:

instead of Session everywhere, you can use a ReactiveDict like that

var rdict = new ReactiveDict("namespace");

And then

rdict.set("foo", bar);
var foo = rdict.get("foo"); 

as usual.

Please note that it has the same benefits as Session, ie the keys/value are preserved between hot code pushes.
Session is implemented that way with

Session = new ReactiveDict("Session")

The main benefit is that your data is now namespaced and not global anymore.

To use ReactiveDict, you have to include it in your app, package name “reactive-dict”

5 Likes

In addition to ReactiveDict I would like to mention ReactiveVar. You can install it by meteor add reactive-var.

After installing you can do:

var rValue = new ReactiveVar("initial value of any type"),
    value;

rValue.set("process");
value = rValue.get();
3 Likes

Here’s another tidbit that is undocumented (anywhere I can find anyway) that I just figured out:

if (!('somepub' in Meteor.server.publish_handlers) {
    Meteor.publish('somepub', function() {
        ...
    }
}

This essentially does the check from the Meteor core code inside your code so you don’t get the message Ignoring duplicated publish named 'somepub'. This is useful when you have code that creates collections/publications based on a variable name.

I found this by doing:

if (Meteor.isServer) console.log(Meteor);

It turns out there is quite a bit of information stored in the Meteor object. I’d highly suggest examining it sometime by dumping the object to the console in this manner. It may be useful in demystifying some of the magic of Meteor.

2 Likes

I had an epiphany or two, recently, when I finally got around to reading up on Blaze – like, properly.

Just reading the Blaze section of the docs for ten minutes, instead of scrolling past it, provided me with a bunch of powerful tricks that I never knew existed. But even more rewarding still is all the background stuff on the Subprojects site, which is an absolute pleasure to read – great work! :clap:

Enlightened, I have set out and combined some of the latest feats of Meteor’s views in a simple technique for rendering- and interacting with modals in the DOM – and I think it works pretty well. Most importantly, it bypasses the limitations that have so far crushed my attempts at proper two-way integration of modals from e.g. Semantic UI.

.
The method leverages the ability of Blaze.render to output a template anywhere in the DOM – such as in a dimmable wrapper directly inside <body> – while maintaining a parent-child relationship between the view of the opening template and the view of the modal. This relationship is required for them to communicate directly.

(Bear in mind that a Session variable, among other limitations, can only hold serializable data…)

With the hierarchy intact, it’s possible to set up shared reactivity between the modal and the background and call methods on the other, too. If we volunteered to use Blaze.renderWithData, then the two templates can even share the same data context! :dancers:

Manually traversing the view hierarchy is pretty tedious, but with the parent() method from aldeed:template-extension), it isn’t an issue.

Depending on whether one likes to use template instances as a ‘view-model’ or prefers to pass stuff around, reciprocal reactivity between the two templates can either be mediated by a new ReactiveVar assigned to a property on the parent template instance or view, or by a more retrieve-on-demand TemplateVar.

(Reminds me of the discussion in Angular land about $scope vs. this… *shudder*)

.
The entire exercise with Blaze.render, that I’ve described above, can of course be duly incapsulated by using tricks like block helper templates and keyword arguments, so that everything is kept declarative.

4 Likes

Very cool! Do you have any sample code for this? I just stumbled across the same problem with semantic-ui’s modal dialogs. It would be really nice if the semantic modal template could use helpers and events like any other Blaze template. I haven’t dug deep enough into Blaze to know exactly why the modals reactivity doesn’t work, but it seems you have ;).

@deb, I agree with @spiralis - do you have a repository where you have done the modal two-way integration? It seems like this would solve some of the issues I have been having (using Session variables to communicate data on modal closing). Perhaps we could collaborate to create an actually nice modal package (I am using one right now that is not entirely ideal).

This could be encapsulated somewhat nicer through a more low-level handling of content blocks. Anyway, here’s a preliminary sketch for getting started. It uses the packages template-extension (for hooks and parent() method) and template-var.

First, the layout file should include a dimmer template:

<template name="appDimmer">
  <div class="ui dimmer modals page"></div>
</template>

Optionally, a reference to the dimmer view is saved in the global namespace (or some place more clever):

Template.appDimmer.hooks({
  rendered: function () {
    // Save reference to this view in global namespace
    appDimmer = this;
  }
});

When a new route is triggered, all open modals should be closed:

Router.configure({
  onBeforeAction: function () {
    if (Meteor.isClient) {
      // Hide active modals using the jQuery method from Semantic UI
      if (typeof appDimmer !== "undefined")
        $(appDimmer.firstNode).children().modal("hide");
    }

    this.next();
  }
});

Each specific modal’s content is wrapped in a custom block helper that takes care of the Semantic UI logic for showing and closing the modal, which looks like this:

<template name="appModal">
  <aside class="ui small modal">
    {{> Template.contentBlock}}
  </aside>
</template>
Template.appModal.hooks({
  rendered: function () {
    var parent_view = this.view.parentView,
        modal = $(this.firstNode);

    modal.modal({
      detachable: false,
      onApprove: function () {
        // Modals are closed manually after successful operation
        return false;
      },
      onHidden: function () {
        // Completely remove modal view after hide transition
        Blaze.remove(parent_view);
      }
    });

    // Show modal immediately upon rendering the template
    modal.modal("show");
  }
});

Each modal is rendered programmatically as the result of some event. The parent template where the event is triggered may have a template variable set up with some value that can be reached by the child modal:

<template name="appMyPage">
  <section class="ui segment">
    ...

    <button id="app-my-action-modal-show" class="ui positive right labeled icon button"><i class="send icon"></i>{{_ 'send'}}...</button>
  </section>
</template>
Template.appMyPage.hooks({
  created: function () {
    TemplateVar.set(this, "value", 42);
  }
});

Template.appMyPage.events({
  "click #app-my-action-modal-show": function (event, template) {
    // (template, context, DOM parent, view parent)
    Blaze.renderWithData(Template.appMyActionModal, this, appDimmer.firstNode, template.view);
  }
});

The actual modal template might look like this:

<template name="appMyActionModal">
  {{#appModal}}
    <div class="content">
      <form id="app-my-action-form" class="ui form">
        <input type="number" value="{{value}}">
      </form>
    </div>

    <div class="actions">
      <button class="ui black deny button">{{_ 'cancel'}}</button>

      <button form="app-my-action-form" class="ui positive right labeled icon approve button"><i class="send icon"></i>{{_ 'send'}}</button>
    </div>
  {{/appModal}}
</template>

With a reactive value from the parent template as well as the event that may close the modal after a remote method call:

Template.appMyActionModal.helpers({
  value: function () {
    return TemplateVar.get(Template.instance().parent(), "value");
  }
});


Template.appMyActionModal.events({
  "submit #app-my-action-form": function (event, template) {
    event.preventDefault();

    var input = template.$("input"),
        form = $(event.target).addClass("loading");

    // The modal view has been opened with the same data context as the parent view,
    // so this._id could be a relevant thing to send
    Meteor.call("myAction", this._id, input.val(), function (error, result) {
      form.removeClass("loading");

      // The view is destroyed through the onHidden hook
      if (!error)
        $(template.firstNode).modal("hide");

      // Alternatively, an error could be shown inside the modal box
    });
  }
});

You’re welcome to get back to me with improvements or questions – thanks :balloon:

1 Like

EDIT: Check out this hackpad for a commented version of the full pattern:

https://meteor.hackpad.com/A-pattern-for-incorporating-modals-wo8Uhynt02r

.
.
.


I improved my pattern above slightly by moving the code for opening new modals into a block helper and using a simple selector for the container element instead of a global reference:

<template name="appModalOpen">
  {{> Template.contentBlock}}
</template>
Template.appModalOpen.events({
  click: function (event, template) {
    Blaze.renderWithData(Template[template.data.template], this, $(".app-modals").get(0), template.view);
  }
});
{{‪#appModalOpen‬ template='appMyPageMyModal'}}
  <button>...
{{/appModalOpen}}

(From this thread: Where to keep global reference to DOM node?)