-
Notifications
You must be signed in to change notification settings - Fork 5.2k
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
New rendered behaviour in Blaze is harmful #2001
Comments
+1 I very much like the idea of an updated() callback. It would be an excellent addition to the API. |
If you need to know when some reactive data changed use can also use a 2014-04-02 17:34 GMT+02:00 Artem Polikarpov notifications@github.com:
|
True, although maybe a person's design involves wiring up a timer to a template, ala the SVG clock demo, and doesn't involve any server-client publications and subscriptions. observeChanges doesn't help if you're just returning a new date value every second. Or, better yet, returning a new date value on window resize. |
My post was about a Deps.autorun in the template :-) 2014-04-02 17:49 GMT+02:00 Abigail Watson notifications@github.com:
|
How would that work? Can you provide a pattern or example? Here's a use case: <template name="homePage">
<div id="homePage">
<h2>{{title}}</h2>
{{resized}}
</div>
</template> Session.setDefault('resize', null);
Meteor.startup(function () {
$(window).resize(function(evt) {
Session.set("resize", new Date());
});
});
Template.homePage.resized = function(){
return Session.get('resize');
}
Session.setDefault('pageTitle', "Home Page");
Template.homePage.title = function(){
return Session.get('pageTitle');
} The goal is to fire something when both the title is changed, and when the page is resized. Like so: Template.homePage.updated() = function(){
console.log('template updated at: ' + new Date());
} How would we do that with Deps.autorun? I don't understand how that could be done. |
Put Deps.autorun in rendered() On any data change these function get called again. Maybe due change of the Am Mittwoch, 2. April 2014 schrieb Abigail Watson :
|
Hmmm... so you're suggesting something like this? Template.homePage.rendered = function(){
Deps.autorun(function(){
var titleText = Session.get('pageTitle');
var resizeDate= Session.get('resize');
console.log('Page title was ' + titleText + 'when resized at ' + resizeDate);
});
} Well, that's not too terrible. I feel like it might be okay for my purposes, since I tend to use reactive context fairly frequently. It would break down with something like this though:
Hmmm... |
If you put a var handler;
Template.homePage.rendered = function(){
handler = Deps.autorun(function(){
var titleText = Session.get('pageTitle');
var resizeDate= Session.get('resize');
console.log('Page title was ' + titleText + 'when resized at ' + resizeDate);
});
};
Template.homePage.destroyed = function() {
handler && handler.stop();
}; Otherwise you'll have memory leaks. |
I've been trying to port a lot of things to the new rendered semantics (sharejs, autocomplete, tutorials) yet it seems in many ways the only way to go is to write my own Putting a Moreover, the porting to helpers from
|
And the component API is going to considerably change in the coming weeks/months... |
I've spent a couple of days trying to figure out how to write some existing packages under the new semantics, and it's been incredibly difficult without any documentation for To summarize, the two approaches that have been proposed are both problematic because
|
If you do put |
Okay, looking at this a bit more, I think there may be a much cleaner workaround, based on the resize() pattern, that doesn't require Deps.autorun. Create a hidden div in your template, and add a field called updated (or whatever). <template name="fooBar">
<div id="fooBar" class="panel">
<h2>{{ getTitle}}</h2>
<p>{{ getText}}</p>
<div id="carousel"></div>
<div>
<div class="hidden">{{ updated }}</div>
</template> On each field that you want to trigger a rerender, opt into the update function by setting the 'updated' session variable with the current date. Then return the 'updated' session variable to the hidden field in the template. Session.setDefault("updated", null);
Template.fooBar.getTitle = function(){
Session.set("updated", new Date());
return this.title;
};
Template.fooBar.getText = function(){
Session.set("updated", new Date());
return this.text;
};
Template.fooBar.updated = function(){
// add stuff you want to happen on each rerender here
console.log('Page title was ' + this.title + 'when updated at ' + Session.get("updated"));
// for instance,
$('#carousel').owlCarousel();
// this is where the rerendering magic happens
return Session.get('updated');
};
// generalize the pattern with the following
UI.registerHelper('updated', function(){
return Session.get('updated');
}); |
+1 I haven't seen any feasible workarounds without Deps.autorun() boilerplate or some hacky techniques, we need an |
Why is it important to know when a text node changes, or when any of several different text nodes changes? I'd like to hear more about that. @mizzao, agreed, the modularity and encapsulation story for components is terrible right now. We're working on it. We had to cut a lot to ship 0.8. In retrospect, it was premature to even implement some of the Component machinery that currently operates under the hood, since a lot of it wasn't right. There's a short list of what people really need to write components, like local reactive state. Let's not get too into that on this thread. That said, I'm curious how it helps component authors to know when part of a template is being updated. Presumably a component does have reactive access to its inputs. I suspect there's a Deps-level solution to whatever the use case is here, i.e. a solution at the level of the view-model rather than bouncing the reactivity off of the template. |
Hi @dgreensp, I don't completely understand your last sentence, but one of the things that's very frustrating with custom components right now is the inability to embox fields of the data context. Usually these are passed in from reactive variables, but right now they are just accessed as constants and there's no way to I don't understand why
unless I'm missing something. Components written as part of apps can access reactive variables in the app, but components that are part of smart packages certainly can't. |
Thanks @dgreensp. A simple example would be handling CSS transitions. If a name or title changes I need an event hook to remove and re-add a CSS class to highlight it. A more complex example would be using Google maps to display a list of places. Meteor renders the map the first time then forgets about it, and I can add the map markers on template.rendered(), but when the place data changes I need a way to update the map markers. I don't know if it's necessary to know when a specific DOM node is changed, maybe an event should fire when a template's data changes. |
@swese44 I don't think your Google maps example needs the I'm just asking for a better way of telling when inputs have changed, because those are usually the ones that we can't easily listen to right now. |
Generally speaking, it's about a) updating CSS presentation when the underlying object model changes, or b) doing machine-to-machine communications and triggering console logging, hardware API access, etc. That's what was great about the old rendered() behavior. It's interesting that you refer to it as the 'level of the view-model'. If by 'view-model' you mean something to update the CSS based on what's in the HTML, then yes... I totally agree. And it's interesting that swese44 just brought up Google Maps, because I was going to mention the Parties example as being a good example of the kind of app UI that people want to develop using updated(). |
The place I ran into needing it is using jQuery x-editable. After x-editable's been rendered, it stores the text in memory. So if the element's text has changed, x-editable needs to be reinitialized, or else when you go to edit it, the previous text is inserting into x-editable's input box. This is debatable about who's problem it is (x-editable or Meteor). X-editable worked amazing in Spark but it is nothing but hell with Blaze. Blaze's dependency tracking breaks if something other than Blaze edit's the text of an element (#1998). |
@mizzao - Maybe people have their templates hooked up to timers, or mobile device inputs, and want the templates to update based on events that have nothing to do with collections. If I put in a user interface element on my page that shows mobile device connectivity status and the current time (which I have done in multiple apps), and want to resize all my elements based on tablet orientation, and adjust theme based on time of day (other things I've done in multiple apps)... those UI rerenders have anything to do with collections, so observeChanges doesn't help. Basically, this latest change breaks tons of functionality with regards to mobile devices and animations. In short, it leans towards a server-side understanding of MVC, and leaves client-side developers a bit in the lurch. But maybe this is paving the way for Famo.us integration. In which case, maybe it's a necessary breaking change in order to get really great animations and mobile device support. Regardless, an updated() API call would go a long way to letting people repair that functionality. |
@davidworkman9 I believe x-editable actually has the ability to update its value from the DOM element, you just have to wire that up correctly, which you didn't have to do in Spark because the entire HTML node would get re-rendered. @awatson1978 I was just giving @swese44 one example for his use case. I'm not convinced that the things you are mentioning need to be done in an |
Thanks @mizzao, that's pretty clear. You're right, there's no API right now where templates get access to the data context as a reactive data source. Only compiled templates do it, under the hood. We've been planning to do something about this for a while (as part of giving templates local state), but this discussion definitely helps clarify the issues for me. My hope is to make the code that templates compile into so transparent that it's easy to drop in and do something different, like reactively capture part of the data context and set innerText on an element if you want. |
Keep in mind that And as for API semantics, have it run |
I'm not seeing any clear way to migrate my sharejs and tutorials packages right now, and autocomplete has been done in a really ugly way due to my inability to find the right way to work with components. @dgreensp: If we could find some way to come up with an API for this on a development branch, I'd be happy to try and use it and give feedback as I update my packages with UI components. @awatson1978: I had an earlier discussion with either @glasser or @avital (can't find the reference at the moment) where I pointed out that the |
What about something inspired along the lines of First, it would be nice to assume that we didn't need to add hidden handlebar fields <template name="fooBar">
<div id="fooBar" class="panel">
<h2>{{ getTitle}}</h2>
<p>{{ getText}}</p>
<div id="carousel"></div>
<div>
<!-- <div class="hidden">{{updated}}</div> -->
<!-- <div class="hidden">{{resized}}</div> -->
</template> And thinking through different options.... Template.fooBar.getTitle = function(){
// Meteor.flush();
// Template.flush();
// Template.flush("fooBar");
// this.flush("fooBar");
// this.update("fooBar");
// this.onUpdate("fooBar");
this.onUpdate();
return this.title;
};
Template.fooBar.getText = function(){
// Meteor.flush();
// Template.flush();
// Template.flush("fooBar");
// this.flush("fooBar");
// this.update("fooBar");
// this.onUpdate("fooBar");
this.onUpdate();
return this.text;
};
Template.fooBar.onUpdate = function(){
console.log('Page title was ' + this.title + 'when updated at ' + Session.get("updated"));
$('#carousel').owlCarousel();
}; Would something like that work? It adds a fair bit of overhead in writing boilerplate, if your basic assumption is that you want the old-style Spark rerendering behavior. But it's not too onerous. |
@awatson1978 could your resizing be handled more efficiently with CSS media queries? |
@swese44 - CSS media queries were my starting point. ;) The resizing pattern is for when you're wanting to get really complex resizing and layout, and use custom layout functions (ie. algebraic and trigonometric layout, laying out elements using sin, cosin, golden ratio, etc) and apply those functions to dozens or hundreds of objects on screen at once. Think D3 style page layouts. But yes... media queries are ideal to begin with, and are an essential part of layout. They hit a wall at a certain point, though. |
I'm closing this and opening #2010. The rendered callback isn't a good way to react to a template argument changing. |
It’s great that instances are never re-rendered in the new rendering model. But it’s really harmfull that it only fires once per template instance.
The example of how to port to the new rendered semantics is really poor and irrelevant to the real life. I don’t want to put every variable to a separate instance, or blowup my code with helpers, or collect all template data sources in Deps.autorun just to know that a template is updated. It really blocks me from upgrade!
Why don’t provide some callback for it,
updated
for example?Mmm?
The text was updated successfully, but these errors were encountered: