Remove inserted template in meteor 0.8.0
Asked Answered
L

5

8

I am inserting templates using UI.render() and UI.insert().

When I tried to remove the template I inserted, it seems to stay in memory and the destroyed method is not called.

According to the documentation, it should clean up property if I use jquery to remove the element.

I'm testing it using the following code:

test.html:

<head>
  <title>removeTest</title>
    <style>
        #content {
            height: 500px;
            width: 500px;
            background-color: gray;
        }
    </style>
</head>    

<body><div id="content"></div></body>    

<template name="foo"><div id="{{id}}">Foo</div></template>

test.js:

if (Meteor.isClient) {
    UI.body.events({
        "click": function(event) {
            var instance = UI.renderWithData(Template.foo, { });
            UI.insert(instance, $("#content")[0]);
        }
    });    

    Template.foo.created = function() {
        this.data.id = "handle";
        var self = this;
        this.x = setInterval(function() { console.log("running...") }, 1000);
        setTimeout(function() { $("#handle").remove() }, 1000);
    };    

    Template.foo.destroyed = function() {
        // never called.
        clearTimeout(this.x);
    };
}

What did I do wrong?

Thanks.

Lore answered 7/4, 2014 at 8:52 Comment(7)
Could you provide more information on why you are rendering the template manually rather than through handlebars? Check your browser console for an error such as "Uncaught TypeError: Cannot set property 'id' of null". The UI.render function sets the data context to null. You could probably make it work with UI.renderWithData, but I suspect there is a more elegant solution using handlebars and letting Meteor handle the lower level rendering as normal.Acidify
I'm writing an app that will dynamically create nodes based on use interaction (kind of like dhtml). The id part works fine. The div is removed when the setTimeout is called. It's just that the destroyed callback was not invoked. Maybe the .remove() does not have the hook to invoke the destroyed callback?Lore
I take a further look into the source, seems like there is a bug in meteor. There are two callbacks subscribe to onRemove, first one call attrUpdater.stop(); and another one call rangeRemoved(range); The anonymous function with attrUpdater.stop() was called but the anonymous function with rangeRemoved(range) was not. rangeRemoved(range) is the one that invoke the destroyed callback.Lore
That should be doable without using UI.render. You should be able to use a helper function to return a cursor from a local collection to drive the rendering of the templates with an {{#each}} block. Then your click event can just insert into the local collection, and your destroyed function will be called when the associated document is removed from the collection (by the timeout).Acidify
Thanks. Yes, it can actually be done using {{#each}} block. The destroyed function will be called in that case. The original issue still exists thou, and I think it is a bug in current implementation. I have filed an issue on github regarding that.Lore
Can you post a link here to the issue so I can follow it? I have the same problem.Intermixture
The issue is closed as the code I have does not consider that the template does not need to have only one root tag, meaning the template have no way to know $("#handle").remove() is intend to remove the template. It only removes the first child of the template. Please see @pent answer to see if that solves your problem. Here is the meteor issue: https://github.com/meteor/meteor/issues/2021Lore
R
5

Some options to remove inserted templates:

a) Using a close event in your template.

Template.foo.events({
  'click a.close' : function(e, t) {
    e.preventDefault();

    t.__component__.dom.remove();
    return false;
  }
});

b) Using a helper, and instance reference

Template.foo.helpers({
  destroy: function() {
    this.dom.remove();
  }
});

var instance = UI.renderWithData(Template.foo, { });
UI.insert(instance, $("#content")[0]);
instance.destroy();
Ruck answered 26/6, 2014 at 14:6 Comment(2)
instance.destroy() did not work for me on 0.8.2, but instance.dom.remove() did.Vip
the instance.destroy() that i'm using, depends on the helper created earlier. That helper method is calling instance.dom.remove() just to make it a bit more cleaner in the controllers. I guess you didn't implement the helper correctly.Ruck
F
2

This is currently a bug in Meteor and is being tracked in the following related GitHub issues:

It should be fixed when updates to Blaze are released.

Freehand answered 13/5, 2014 at 21:35 Comment(0)
B
1

According to Avital back on April 19, the code is being rewritten (source).

In the meantime, if you take a look at the properties of your node element $("#handle")[0], you'll see you have one called $ui which corresponds to the DomRange object (code). If you call remove on the DomRange object, your destroyed callback will fire. In fact, it will fire the callbacks for any nested templates as well.

$("#handle")[0].$ui.remove()
Bloodsucker answered 13/5, 2014 at 13:52 Comment(0)
S
1

I am on meteor 1.0.3.2, so it is possible this solution was not available at the time the question was asked. Blaze actually provides a remove method that removes a previously rendered template view and invokes the destroyedcallback.

Your code would look like this:

Template.foo.rendered = function() {
  var self = this;
  this.x = setInterval(function() { console.log("running...") }, 1000);
  setTimeout((function() {
    UI.remove(self.view);
  }), 1000);
};

Template.foo.destroyed = function() {
  return clearTimeout(this.x);
};

Note that you probably want to use the created callback instead of the rendered callback. This is because remove expects a view already rendered in the DOM. You can read more about the the difference between those two callbacks here: http://meteor.github.io/blaze/docs.html#template_rendered

And for more reading on the UI functionality that Blaze gives you, refer here http://docs.meteor.com/#/full/blaze_remove.

Schreiner answered 4/4, 2015 at 4:10 Comment(0)
A
0

I ran into this problem recently as well. A fix right now until Meteor supplies a patch, would be to change the removal function to:

setTimeout(function() { 
  $("#handle").remove() 
  self.__component__.isRemoved = true;
}, 1000);

As for the clearTimeout... we'll have to wait for the patch I think

Adams answered 10/4, 2014 at 23:48 Comment(1)
Great. That shows meteor should have a way to handle this nicely. I have teardown my environment so I couldn't verify your solution. Do you have any demo project that I can take a quick look at? I would like to mark your answer as solution.Lore

© 2022 - 2024 — McMap. All rights reserved.