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

Reload template contents without refreshing page. #543

Closed
xcoderzach opened this issue Apr 2, 2014 · 23 comments
Closed

Reload template contents without refreshing page. #543

xcoderzach opened this issue Apr 2, 2014 · 23 comments
Milestone

Comments

@xcoderzach
Copy link

I'm trying to setup live template reloading, is there a way to reset the contents of the ractive template, without creating a new one?

@martypdx
Copy link
Contributor

martypdx commented Apr 2, 2014

Just change the top-level data object? Not sure what you're going for. If you had:

<ul>
    {{#items}}<li>{{.}}</li>{{/items}}
</ul>
var ractive = new Ractive({
    ...,
    data: { items: [1,2,3] }
})

And later on you did:
ractive.set('items', [4,5,6])

It's going to re-use the same template.

@xcoderzach
Copy link
Author

Hah, sorry poor explanation. I actually want to do the opposite of that, I'd like the data to stay the same, but the change the template. However, doing

ractive.template = "my new template"

doesn't work, obviously.

@martypdx
Copy link
Contributor

martypdx commented Apr 2, 2014

A new template would need to be re-rendered, so I'm still not clear what you want to gain.

Are you wanting to Carry forward the current state of the data? Is it dynamic, or are the templates you want to switch to or between known ahead of time?

@Rich-Harris
Copy link
Member

This sounds similar to #422 and #459. Have I understood correctly @xcoderzach - that this is basically about 'routes', and switching from one to another seamlessly?

It seems like something that should probably be supported in future, possibly through a plugin. I offered one solution on StackOverflow, I don't know if that's helpful here.

@Rich-Harris Rich-Harris added this to the post-0.4.0 milestone Apr 7, 2014
@martypdx
Copy link
Contributor

martypdx commented Apr 7, 2014

Ah, yes! I knew there was a way to set template in beforeInit. Just forgot it has to be done in a component as there is no beforeInit on base Ractive.

@codler
Copy link
Member

codler commented Apr 7, 2014

It would be good to have beforeInit in documentation

@Rich-Harris
Copy link
Member

Agreed. Am in the process of giving the docs (and the rest of *.ractivejs.org) a spring clean (http://docs.ractivejs.org/preview) - as soon as that's done we'll be able to start plugging some of the holes in the documentation.

@codler
Copy link
Member

codler commented Apr 7, 2014

Awesome, I find it much easier to read and navigate in the new design.

@Rich-Harris
Copy link
Member

@codler You have @browniefed to thank for that, he replaced the guts of the docs repo with http://assemble.io/ and implemented the nav sidebar

@codler
Copy link
Member

codler commented Apr 7, 2014

Thank you @browniefed! :)

@jonathantneal
Copy link

I’m sorry for deleting my comment and code with beforeInit before your reply @martypdx. I removed it because it did not address the issue of dynamically updating the template. Here it is, for reference.

Ractive.components.tpl = Ractive.extend({
    beforeInit: function (self) {
        self.template = self.data.src;
    }
});

ractive = new Ractive({
    el: document.body,
    template: '<tpl src="{{tpls.foo}}" />',
    data: {
        tpls: {
                foo: 'Hello, <b>{{text}}</b>!',
                bar: 'Goodbye, <b>{{text}}</b>!'
        },
        text: 'Ractive'
    }
});

@browniefed
Copy link

@jonathantneal you have to set a partial

Ractive.components.tpl = Ractive.extend({
   template: '{{>template}}',
    beforeInit: function (self) {
        self.partials.template = self.data.src;
    }
});

Edit: actually I'm wrong that should technically work, not 100% certain but template may be parsed before the beforeInit is called and thus nothing exists there. I haven't actually looked at that code.

@browniefed
Copy link

@codler no problem, it was easier to convert it to assemble than it was to keep looking through docs in the format that they were in.

@xcoderzach
Copy link
Author

What I'm trying to build is LiveReload (see gruntjs/grunt-contrib-watch or mechio/takana) support for Ractive. Currently, when I make a change to a ractive template, my LiveReload refreshes the entire page.

What I'd like to happen is: when I make a change to a template, the content of the template is updated without reloading the entire page. Also maintain state and reattach any DOM events.

Currently the way I work is I do my "designy" code in a separate static html file, and then, when it looks good, convert it to a Ractive template. It would be awesome if I could get quick visual feedback while writing Ractive templates.

@Rich-Harris
Copy link
Member

That sounds awesome. I'm not sure to what extend that should be a first class feature of the library, but I can certainly see it having widespread utility.

Here's one approach: http://jsfiddle.net/rich_harris/v8GX9/

Basically, it exploits the fact that partials are lazily parsed at render time - if, when rendering {{>foo}}, it turns out that ractive.partials.foo is a string, then it gets parsed once. But if you overwrite ractive.partials.foo with another string, it will get reparsed the next time the partial is rendered.

You could easily break this by doing Ractive.extend() again and overwriting the logic that initialises and updates the template/partial, but in many situations that wouldn't be necessary.

@jonathantneal
Copy link

So, borrowing from what has been said, how about:

Ractive.components.tpl = Ractive.extend({
    template: '{{#init}}{{>tpl}}{{/init}}',
    init: function () {
        this.observe(this.data.key, function (tpl) {
            this.set('init', false);

            this.partials.tpl = tpl;

            this.set('init', true);
        });
    }
});

// example
ractive = new Ractive({
    el: document.body,
    template: '<tpl key="tplA" /><tpl key="tplB" />',
    data: {
        tplA: 'Hello, <b>{{text}}</b>!',
        tplB: 'Goodbye, <b>{{text}}</b>!',
        text: 'World'
    }
});

// example dynamic updates
setTimeout(function () { ractive.set('tplA', 'Hello, <b>{{text}}</b>!!!!!?!'); }, 1000);

setTimeout(function () { ractive.set('text', 'Ractive'); }, 2000);

@martypdx
Copy link
Contributor

martypdx commented Apr 7, 2014

Ok, so a side project I've been limping along to finish is a rewrite of a real-time template editor I did a few years back. Think jsfiddle or codepen, but integrated into your development environment, component aware, and with data as a first class construct. You can see the component editor here:

http://3de-io.github.io/3DE/src/index.html

(if you don't like jade or stylus, hover on the target in lower left and choose mustache or css. Bottom right is for js and uses CTRL+enter instead of realtime, you write your component extend, include component.exports = {} ).

Important bit here is that Ractive is fast enough to re-render real-time. You can just teardown existing instance and create a new one. It's a bit hacked up right now, but you can check out https://github.com/3DE-io/3DE/blob/master/src/layout.html to see what I do.

@martypdx
Copy link
Contributor

martypdx commented Apr 8, 2014

I was rushing out the door when commented earlier. Here's a an example of a very simple render that updates the options and re-renders. css, data, etc. stay intact and if you are consuming components you don't have to re Ractive.load them

var ractive, 
    options = {
        el: 'main',
        data: { name: 'world' }
        //any other options
    }

function render(template){
    if(ractive){ractive.teardown()}
    options.template = template
    ractive = new Ractive(options)
}

//your update event... render(template)

see it in action.

@jonathantneal
Copy link

If you could update your templates without tearing down the entire instance, I imagine that would be preferred. Perhaps we just need something like an updatePartial and updateTemplate method. Unless there are other, better ways to have and utilize dynamic templates?

@Rich-Harris
Copy link
Member

I've been thinking about how far we could take this idea. I think there are a range of situations where it would make sense to do a diff between the two templates and update the view accordingly (e.g. inserting new elements in the correct place) - @xcoderzach's case is one, @martypdx's template editor is another.

As it happens I'm currently working on a third - a markup-driven dataviz tool with a CodeMirror editor. At present it has to tear everything down and create a new instance on each change (albeit debounced), and it's perceptible because these are often fairly complex visualisations. A fourth example would be the demo widget on http://ractivejs.org.

So there's a variety of uses for non-destructive template updates. But it ought to be a plugin rather than part of the core library, because it's still a fairly niche requirement, and the ability to diff the parsed templates is a non-trivial addition. It would probably make sense to use an existing JSON diffing library like https://github.com/benjamine/jsondiffpatch.

But that means that some of Ractive's guts need to be exposed to the public. So it would require a bit of thought about what that API should look like.

Vaguely relevant issue: #395.

On a less ambitious note, the idea of dynamically updating partials (as opposed to entire templates) has been around a while. I'd like to make some progress on that before too long; it would make some of this stuff easier in the meantime. (A few people have suggested that {{>foo}} should render a partial defined by ractive.get('foo'), which re-renders when the value of foo changes. The idea has some merit, and in a way it's consistent with how triple-staches work, but I'm inclined to prefer @jonathantneal's updatePartial suggestion. You could say I'm more partial to it.)

@jonathantneal
Copy link

What is the status of updatePartial and like-minded ideas?

@Rich-Harris
Copy link
Member

There's a resetTemplate() method on the dev branch: http://jsfiddle.net/rich_harris/HuY2n/

It's a bit of a blunderbuss at the moment - it unrenders everything then re-renders from scratch. There isn't yet an updatePartial() method but I definitely think we should add one, probably after the forthcoming release. There was also some recent work that heavily refactored the whole rendering process (#696) which should one day make it easier to implement non-destructive versions of these methods.

@evs-chris
Copy link
Contributor

Is it safe to say that #1424 resolves this?

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

No branches or pull requests

7 participants