Access template instance object properties directly from Spacebars - javascript

As the Meteor docs state,
A template instance object represents an occurrence of a template in the document. It can be used to access the DOM and it can be assigned properties that persist as the template is reactively updated. [...] you can assign additional properties of your choice to the object.
How can these properties be accessed from Spacebar templates?
It's cumbersome to have to define helpers that do nothing more than
return Template.instance().myProperty

Just like #apendua said in comment, you can use global helper, this worked for me:
Template.mainPage.onCreated(function(){
this.test = new ReactiveVar('stackoverflow');
});
Template.registerHelper('instance', function(context, options) {
return Template.instance()[context].get();
});
and in HTML
{{instance 'test'}}
You could probably use Template.currentData() as well.
Edit
According to this article you can do in HTML something like:
{{Template.instance.test.get}}
so there is no need for global helpers, and it is accessible from Spacebars so no helpers required

OP wants to access the data directly without any helpers. You will need to store the data in Template.currentData() or Template.parentData(n). Anything in those can be accessed as {{myData}} or {{../myParentData}}, respectively.

I think you can use {{#with}}{{/with}}
for example
{{#with getObject}}
<h1>{{prop1}}</h1>
<h2>{{prop2}}</h2>
{{/with}}
and in helper
getObject:function(){
return {prop1:'some text prop1',prop2:'some text prop2'}
}

Related

Blaze Meteor dynamically instanciate template and datacontext

I'm dynamically instanciating template on event / or array change (with observe-like functionality).
To achieve that, I use
//whatever event you want, eg:
$(".foo").on("click", function(){
Blaze.renderWithData(Template.widgetCard, d, $(".cards").get(0));
}
That is working, but obviously, instances aren't bound to any parent's template.
Because I just rendered this template on the div.cards I'm unable to use the Template.parentData(1) to get the parent datacontext, even so this div.cards is include on a template.
The quick fix would be to set the wanted reference (which in my case is an object) variable parent's datacontext on global scope, or even use Session, or directly pass this context through the renderWithData's data.
Do you know any other way,even better the proper one (I mean Meteor fancy one), to achieve that?
Is it a good Blaze.renderWithData use case?
Tell me if i'm unclear or more code is needed.
EDIT:
Complementary context info:
I've a chart (d3) where it's possible to select some parts of it.
It has an array property to stock this selected data part.
Chart = function Chart(clickCb, hoverCb, leaveCb, addSelectionCb, removeSelectionCb){
var chart = this;
chart.selectedParts = [];
//... code
}
From outside of this Chart class (so on the Meteor client side), the chart.selectedParts is modified (add/delete).
The dream would be to "bind" this array chart.selectedParts like:
Template.templateContainingAllThoseCards.helpers({
selectedDataChart: function(){
return Template.instance.chart.selectedParts;
},
//...
});
and on the template being able to do something like that:
<div class="row">
<div class="large-12 columns">
<div class="cards">
{{#each selectedDataChart}}
{{> cardWidget}}
{{/each}}
</div>
</div>
</div>
Like that, if the chart.selectedParts was reactive, Blaze could automatically create or remove cardWidget template instance due to the binding.
I've tried to use manuel:reactivearray package on it (and it's kind of anoying cause I'm doing complex manipulation on this array with Underscore, which obviously don't work with none-native Array type such reactiveArray).
Not working, but I dunno if it should have worked.
What do you think?
At this time, I'm doing things a bit dirty I suppose; I juste instanciate/destroying Blaze View on element added/removed chart.selectedParts as: Blaze.renderWithData(Template.widgetCard, {data: d, chart: this}, $(".cards").get(0));
So here how I manage to do that.
Actually I don't think using Blaze.renderWithData() is a good solution.
Best way I've found is to pass your data on "Reactive mode", to be able to use all Template functionalities, and keep using Spacebars to instanciate templates. (Like parent DataContext link).
Easiest way to have reactive datasource is to always match your data with your Mongo, so I don't have to declare a custom Reactive Data source (which could be tricky with complex from a complex js data structure).
If someone have the same problem, I'm pretty sure it's because you don't follow the "good" way to do (which was my case).
One con with always updating your DB as reactive Data source should be a case where you're doing a lot of UI state change, and after all, saving the change. On this case, it's pretty useless to always pass by the DB, but it's from far the quickest solution.
Ask me if you have any similar issue understanding philosophy/way to do, I'm starting to understand what i'm doing!

get access to variable in directive from controller

I created a directive, I´m using in my template:
<data-input> </data-input>
In my directive (dataInput) I have the template (...data-input.html).
In that template the html-code says:
<input ng-change="dataChanged()" ... > ...
In the JavaScript it says:
scope.dataChanged= function(){ //change the data }
Works perfectly, but know I need to safe the changed data to a variable in my controller (that has a different scope of course).
The template, where I put the <data-input> </data-input> belongs to the final scope, I´m trying to reach.
I tried it via parameter, but it didnt work.
Thanks
Here are your options:
You can nest controllers if possible. This will create scope inheritance and you will be able to share variables of the parent.
You can use $rootscope from both the controllers to share data. Your dataChanged function can save anything to the $rootscope
You can use angular.element to get the scope of any element. angular.element('data-input').scope() returns it's cope.
This is not recommended. But there are circumstances in which people use global space to communicate between angular and non-angular code. But I don't think this is your case.
You can use Angular Watches to see changes is some variable, like this
eg:
$scope.$watch('age + name', function () {
//called when variables 'name' or 'age' changed
//Or you can use just 'age'
});

How to access template instance from helpers in Meteor 0.8.0 Blaze

The change in behavior for the Template.foo.rendered callback in Meteor 0.8.0 means that we don't get to automatically use the rendered callback as a way to manipulate the DOM whenever the contents of the template change. One way to achieve this is by using reactive helpers as in https://github.com/avital/meteor-ui-new-rendered-callback. The reactive helpers should theoretically help performance by only being triggered when relevant items change.
However, there is now a new problem: the helper no longer has access to the template instance, like the rendered callback used to. This means that anything used to maintain state on the template instance cannot be done by helpers.
Is there a way to access both the template instance's state as well as use reactive helpers to trigger DOM updates in Blaze?
In the latest versions you can use more convenient Template.instance() instead.
Now there's Template.instance() which allows you to access a template's instance in helpers. e.g
Template.myTemplate.helpers({
myvalue: function() {
var tmpl = Template.instance();
...
}
});
Along with reactiveDict, you could use them to pass values down set in the rendered callback.
Template.myTemplate.created = function() {
this.templatedata = new ReactiveDict();
}
Template.myTemplate.rendered = function() {
this.templatedata.set("myname", "value");
};
Template.myTemplate.helpers({
myvalue: function() {
var tmpl = Template.instance();
return tmpl.templatedata.get('myname');
}
});
This is currently being tracked as "one of the first things to be added" in post 0.8.0 Meteor:
https://github.com/meteor/meteor/issues/1529
Another related issue is the ability to access data reactively in the rendered callback, which avoids this issue in the first place:
https://github.com/meteor/meteor/issues/2010

pass a dom level js object to a template function using jsrender

I simply want to pass in a javascript object that is populated by the template to a helper function. I have looked at conversations regarding passing in helper objects to loops but have not found anything that directly addresses the following example.
<script type="text/javascript">
// an object set by the template
var SomeObject = {};
SomeObject.Id=6;
</script>
<script id = "SomeTemplate" type="text/html">
<div id="somegroup_{{>Id}}" class="main">
<img id="somegroup_img_{{>Id}}" class="mainImg" src="{{: ~fltrOAMnImgs_hlp(Images, SomeObject)}}">
</div>
</script>
The current above code does not pass SomeObject to the helper func.
How could I do this with the new jsrender lib?
If you want to provide access to your SomeObject within the template, you must pass that object in as a helper. There are some different ways to do that, depending on whether you want it available globally to all templates, just to this template, or just for this render() call. See: the $.views.helpers() topic under Register helpers, converters, tags...
For example if you pass it in with the render call, with a name such as "myObject":
myTemplate.render(myData, {myObject: SomeObject});
then you can reference it as in ~myObject:
<img ... src="{{: ~fltrOAMnImgs_hlp(Images, ~myObject)}}">
See also this related answer: Trying to run a function on an array in a, for loop, using jsrender

How to access data inside if statement in Handlebars templates

To the handlebars (version 1.0.0-rc.3) template I am passing two variables , one is the json and the other one is the string containing the current language on site.
self.template = template({ data: self.model, lang:self.lang });
Then inside the template file I have the following structure:
{{#each data}}
//this is working
{{../lang}}
{{#if this.title}}
{{this.desc}}
//i've tried this
{{../lang}}
//and this
{{lang}}
{{/if}}
{{/each}}
...but I couldn't access the lang value inside the if statement. What am I doing wrong?
I know you already solved your issue with a workaround but registering a Helper for doing a native way is cumbersome.
The thing is that every Handlebars helper overwrites the context and nest the new one inside the parent one, so you have to go up uone step further, like a UNIX like directory.
So, to access lang inside an each->if you have to use:
{{ ../../lang }}
I've find a solution by creating a handlebars helper function:
Handlebars.registerHelper('language', function() {
return self.lang; });
Then in the template i could use {{language}}
where ever I want.

Categories

Resources