Render parts of a template - javascript

I have a Backbone view and I'm using Handlebars to load templates in my view.
I have a list #list in the template that have to be updated in the view and after that it needs to be re-rendered.
I want to re-render only the part that has changed so only #list without reload all my DOM (and so all the Handlebars helpers that I have).
I tried this:
template: Handlebars.compile(templateHtml),
render: function() {
this.$el.html(this.template(this.model.toJSON());
},
renderList: function() {
var html = this.template(this.model.toJSON());
var selector = "#list";
this.$el.find(selector).replaceWith($(selector, html));
}
but this is very slow because I think it re-renders all DOM before changing the #list content as I need.
Is there any better way to do that?
Is it a good idea to put the content of #list in another template and load it as a subView?
In this case how can I trigger an update of the list from my first view?
Thanks

It is better to update just the part of your rendered view by creating a dedicated render method, something I explained here.
In your situation it looks like you have to build whole html view from template, just to update the part of it. Which is neat, if you want to keep the template as one template.
If there really a lot of going on in the template beyond the list, you could have two templates in your view, one will be the general one, and the other one just for the list, which you will mount into the first one on global render, with the benefit of being able to update just the list.
Further, you might not even need a second template if the #list is simply a container for a list. I can imagine that might be an <ul> tag which you will mount at the right place during the global render, and then update it with other custom render methods.
Also, I wrote an example here, which might be potentially helpful in your case:
Backbone.js fetch() JSON to model get() returnes undefined - in the example there's a template, that has a container for a list, and the list is managed separately by it's own collection.
Just some thoughts.

Related

Knockout template and unique ID within complex web app causing problems

Knockout templating system is great, however in a web app where there are several separated contexts ("views") that are loaded by ajax, one issue appears:
Templates rely on ID
This means that if my chance you have one template with the same name on a view that on another view loaded previously and still existing in the webapp context, knockout (because the browser does this) will take the first matching #templateId element.
On our webapp, we eliminated the ID of all our elements, and when it really needs to be used, it's an ID that is javascript determined to not have duplicates.
Some views can be loaded multiple times in the lifetime of the app, so
no, we can't say "simply check if the id is already used before making your html code" to our team members.
The only other thing we could do would be to check if a specific template is loaded, and if not load it in async, then apply bindings. But for simplicity purpose and the way our project is set up right now, we can't apply an js AMD-like dependency manager.
Questions
Is that possible to specify directly the DOM reference to the template directly?
data-bind="template:function(){ return $('yourSelectorToTheTemplate')[0]; }"
I've looked knockout code and it's weird because we have this:
templateDocument = templateDocument || document;
var elem = templateDocument.getElementById(template);
if (!elem)
throw new Error("Cannot find template with ID " + template);
return new ko.templateSources.domElement(elem);
This means that it really use the DOM element, so why being forced to give an ID for it if we already have the ID?
How do we retrieve dynamically applied IDtemplate, that is also calling another dynamically applied ID (template recursively calling itself for example)?
Setting ID from a binding handler may be wrong: it may set the ID after other data bound elements referred to it, but it would be simpler to have to.
The best solution found for the moment:
Place templates (script element) at the top of the html view.
Use a bindingHandler that does initialization (I called mine "init" as you can see in the example below) to set the ID of the script element
Store that ID inside the $root context so it can be reused by other elements
The result looks like this:
<script type="text/html" data-bind="init: function(){ $rawData.folderItemTemplate = functionThatSetsAndReturnsUniqueId($element, 'folderItemTemplate'); }">
<li>
Some item
<ul data-bind="template: { name: $rawData.folderItemTemplate, foreach: children }"></ul>
</li>
</script>
As you can see we can use this template binding with template: { name: $rawData.yourPropertyName, foreach:... }

Backbone Render View on model update

I'm writing a sample app using backbone.js.
On update of my model I re-render my view in this fashion
$('.target').html("");
$('.target').append(this.$el.html(this.template(model)))
Once the view is re-rendered after model update [on change event], events attached to the el's children gets lost [doesn't seem to be like jQuery live]. Is this a known issue or am I missing something? Should I try to replace html instead of append? fiddle
Once the view is in DOM, you don't need to keep removing and appending it. I think the simplest way to manage this is to remove the DOM insertion from the view altogether, and let the caller of view.render to take care of it.
View:
render: function() {
this.$el.html(this.template(model));
return this;
}
Caller (on first render):
var view = new SomeView();
$('.target').append(view.render().el);
On subsequent renders:
view.render();
After the view has been rendered into DOM it can keep happily re-rendering itself without having to know anything about parent views. The event bindings should also stay intact between renders.

backbone not rendering the $el but can reference element directly

This is killing me, being reading the examples on this site but can't figure out why it works like this.
I want to pass back values to my view, which has buttons that you can use to change the values.
If I use the following
this.$el.empty().html(view.el)
View.el contains the correct html, but those not render on the screen. If I use the following
$("#handicap").html( view.el);
The values get displayed on screen but the events no longer get picked up eventhough if I put an onclick function in the html code it kicks off.
Ideally I would like to get this.$el.empty().html(view.el) working. It has to do with context but can't see why.
I have created a jsbin here http://jsbin.com/iritex/1/edit
If I have to use $("#handicap").html( view.el), do I need to do something special to unbind events. I have tried undelegate everything but that didn't do the trick either.
thanks
A Backbone View's el property will always contain a reference to a valid DOM object. However, that DOM object may or may not be in your display tree. It's up to you to make sure it's in the display tree when you need it to be. This functionality lets Backbone maintain the state of it's View element without it being rendered to the screen. You can add and remove a view from the screen efficiently, for example.
There are a few ways to get your View's element into the display tree.
1) Associate the view with an existing DOM element on the page by passing in a jquery selector to the initializer as the "el" property.
var view = new MyView({el: '#MyElementSelector'});
2) Associate the view with an existing DOM element on the page by hardcoding the jQuery selector it into the view's "el" property.
var MyView = Backbone.View.extend({
el: '#MyElementSelector'
});
3) Render it to the page from within another view
var view = new MyView();
view.render();
this.$el.empty().html(view.el);
If you're interested, I show examples in a Backbone Demo I put together.
You need to put both views into the DOM. Wherever you create the view that above is this needs to be inserted into the DOM. If you do that, then the first line will work fine this.$el.empty().html(view.el).

Ember.js / Handlebars: views rendered using {{view}} helper not binding attributes

I have a mysterious issue with Ember/Handlebars views. I am attempting to implement very simple data binding in a Handlebars view. This works correctly when render my view by doing:
Ember.Views.NavView.create().append()
But the bound attribute isn't shown when I attempt to render another instance of the same view using the {{view}} helper, like so:
<script type="text/x-handlebars">
{{view App.Views.NavView}}
</script>
In the first case the attributes (hardcoded on the View for this test-case) are shown correctly. In the second case I get "metamorph-0-start" and "metamorph-0-end" tags, but the value itself is not rendered.
I have set up a JSFiddle (http://jsfiddle.net/XUyht/2/) that illustrates the problem clearly.
You'll see that I have rendered the view twice: the first using the {{view}} helper and the second using append() - but the attribute "working" only shows in the latter case.
What's going on here?
I don't know why you need this kind of implementation of the template, but anyway, since the 1.0-pre, the default context of a view is either its controller, either its parent view. So in your case, if you replace tmp.foo with view.tmp.foo, this is working.
see http://jsfiddle.net/Sly7/amLfk/

Ember.js: How do I hook Ember.CollectionView after every child view is rendered?

This question demonstrates that overriding an Ember.View instance's didInsertElement allows you to execute some code after the view's element is in the DOM.
http://jsfiddle.net/gvUux/2/
Naturally, overriding didInsertElement on the child view class you add to an Ember.CollectionView will run the hook after each child view is rendered and inserted.
http://jsfiddle.net/BFUvK/1/
Two collection-oriented hooks on Ember.CollectionView, arrayDidChange and contentDidChange, execute after the underlying content has changed, but they execute before any rendering takes place. arrayDidChange is executed for every element added to the array, and contentDidChange wraps the content binding.
I would like to be able to hook around the rendering pipeline, something like willInsertCollection and didInsertCollection, to manipulate the DOM before and after all child elements are rendered - essentially, before and after filters around contentBinding.
Any ideas? I'm stumped.
If you want to want to do something before and/or after a view has been rendered you should use willInsertElement and/or didInsertElement respectively. In this case, since you want "to manipulate the DOM before and after all child elements are rendered" you should call those on your CollectionView.
I'm not quite sure what you mean by "before and after filters around contentBinding", so if this doesn't answer your question if you could clarify I'd be happy to help.
jsFiddle if needed
I wanted to apply a scroll animation to slide a list up after pushing new objects. The list was rendered using an ArrayController and the #each helper. Simply triggering an event on the controller which the view subscribed to after pushing objects was causing the animation to execute before the changes to the content were actually rendered. The following technique worked perfectly for me.
//excerpt from my loadMore method on the ArrayController
var self = this;
self.content.pushObjects(moreItems);
Ember.run.scheduleOnce('afterRender', this, function()
{
self.trigger('loadMoreComplete');
});

Categories

Resources