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/
Related
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.
We are currently working on a project, in which we use a combination of AngularJS and Polymer.
We have some structure, but what's really important is this piece of code:
<template is="dom-bind" angupoly="{dataContainer:'dataContainer'}">
<menu-list for="places" data="{{dataContainer.getSomeData()}}">
</template>
We have a variable defined on $scope named dataContainer, which is being set in a controller. The problem is that this code is executed before the controller prepares that property, so it's undefined - it throws:
[dom-bind::_annotatedComputationEffect]: compute method dataContainer.getSomeData() not defined
And the data are never refreshed again and it does not work. On the contrary, with a property it works (it does not matter if it's first state is undefined), it is refreshed.
Because it's a really important point in our application, we wanna ask. How to reach required behaviour?
Thanks, have a nice day! :)
I am not familiar with polymer and if there are possibilties to delay the execution of the polymer code or if there a digest cycles one could work with like in AngularJS.
But I would guess you could avoid this kind of race condition with a simple ng-if= in conjunction with <ng-include> on AngularJS side - as it does not add the elements to the DOM, thus avoid any kind of interaction with polymer until it is included.
So e.g.:
<figure ng-if="dataContainer.getSomeData">
<ng-include src="'template.html'">
</figure>
And within template.html your (unmodified) code:
<template is="dom-bind" angupoly="{dataContainer:'dataContainer'}">
<menu-list for="places" data="{{dataContainer.getSomeData()}}">
</template>
I would be happy if this helps you - but as I said it is just a guess and I don't know much about polymer and how it interacts with the DOM.
The error that you are seeing is not caused by dataContainer being undefined, but the function that you call on it. Polymer does not support this type of function calls in data-binding. From the docs:
Data binding binds a property or sub-property of a custom element (the host element) to a property or attribute of an element in its local DOM (the child or target element).
You are calling a function on a property, which does not work.
If you want to call a function, you could to this with computed bindings.
<menu-list for="places" data="{{getData(dataContainer.*)}}">
However, this assumes that your menu-list is placed in some parent Polymer element and I'm not sure if this is the case here as you use a dom-bind. However, if you do have a parent element you could then add the computed function there.
Polymer({
is: 'parent-element',
properties: {dataContainer: ....},
getData: function() {
return dataContainer.getSomeData();
}
});
getData will be called anytime dataContainer or its sub-properties change.
Hi please explain reason for following three scenarios as I am unable to know why is this happening -
1)
<div ng-controller="myctrl">
<p>something for DOM manipulation</p>
</div>
2)in route I write
('someroute',{
templateUrl : "mytemplate",
controller : "myctrl"
});
mytemplate:
<div>
<p>something for dom manipulation</p>
</div>
3)
<div ng-include="mytemplate" ng-controller="myctrl"></div>
with template being same as above
The controllers in all the above scenarios are same, and in all of them I am just trying to select p tag of DOM by writing angular.element('p'). But this seems inconsistent. It works very well in 2nd scenario, it never works in 3rd scenario and I am not sure about 1st sccenario. Can someone explain which method is best for dom selection/manipulation, as I have to add a class to this 'p' tag on hover.
I am not understanding which gets initialized first- controller or partial?
Manipulating DOM inside controllers is discouraged. Quote from Best Practice - Dom Manipulations:
Dom Manipulations should not exist in controllers, services or anywhere else but in directives.
If you only need to style elements on hover, using the p:hover CSS selector would be enough without touching the DOM. ng-class and ng-mouseover can help you if you really want the class.
For more complex scenarios, you may want to write your own directive. You can check the article above for a guide to do that.
Load order from the first case: HTML first. Directives like ngController are loaded after parsing the HTML. Therefore the HTML already exists when the controller is loaded.
Load order for the second case: I'm not sure about it. You may check documentation for ngRoute or uiRouter depending on the router you are using.
Execution order for the third case: Controller first. The directive ngController have higher priority than the ngInclude directive. Therefore, the controller is loaded first.
Quote from ngController documentation :
This directive executes at priority level 500.
Quote from ngInclude documentation :
This directive executes at priority level 400.
I wanted to ask if it is possible to bind data to the element and access it later inside the actual content. Here is an example: I want to create a list component, however let the user define how to render every entry. Here is my current code:
List Element:
<template repeat="{{item in items}}">
<content></content>
</template>
User using it:
<ak-list items="{{items}}">
{{item.name}}
</ak-list>
However, this does not work
I suppose - you won't get access to data model from inside HTML portion in the web components.
You need to be defining the data in the template. I guess you might be already aware of that.
http://jsbin.com/yadazo/1/edit?html,output
A bin with how it could work.
Also, you can control the presentation by passing in an additional data which you can then use in your template -
An example of the same is below. -
http://jsbin.com/yateka/1/edit?html,output
both the list and how you want it can be supplied and then template created with that stuff.
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).