how to assign function to marionette.template - javascript

As per I know, marionette.template accept either jquery selector or compiled template string.
If I write code like in the following way, working fine
exports.ProductInfoView=Backbone.Marionette.ItemView.extend({
domInfo:{
mainTemplateId:"tagProductListTpl",
tableTemplateId:"taginfoViewTpl"
},
template:commomFunctions.templateCompilation("tagProductListTpl",""),
onRender:function(){
this.templatingProductInformation();
},
modelEvents:{
"change:currentJson":"templatingProductInformation"
},
templatingProductInformation:function(){
console.log(this.el);
//this.el.innerHTML=commomFunctions.templateCompilation(this.ui.mainTemplateId,"");
}
});
Note :commonFunctions.templateCompilation() accept templateId as first argument and data as second argument. It will compile handlebars template and it return compiled template.
If I assign that return value to template, working fine.
I want to make data for templating,so I am passing function to template like in the following way.
exports.ProductInfoView=Backbone.Marionette.ItemView.extend({
domInfo:{
mainTemplateId:"tagProductListTpl",
tableTemplateId:"taginfoViewTpl"
},
template:function(){
return commomFunctions.templateCompilation("tagProductListTpl","");
},
onRender:function(){
this.templatingProductInformation();
},
modelEvents:{
"change:currentJson":"templatingProductInformation"
},
templatingProductInformation:function(){
console.log(this.el);
//this.el.innerHTML=commomFunctions.templateCompilation(this.ui.mainTemplateId,"");
}
});
This way also working fine, If you observer I hard coded templateId("tagProductListTpl") inside function.But I don't want like that. I want to use like this.domInfo.mainTemplateId instead of hard coding. that way it's not working fine.
It's throwing error. I know it's out of scope. but how can I achive this.
can anyone help me.
Thanks.

I will advise you to rewrite Marionette.TemplateCache.prototype.compileTemplate which is responsible for template compilation. Look at this post, there almost the same issue.
Marionette.TemplateCache.prototype.compileTemplate = function (yourRawTemplate) {
// In case if template is function
if (_.isFunction(yourRawTemplate)) {
return yourRawTemplate;
} else {
return Handlebars.compile(yourRawTemplate);
}
};
And if you loading template files from remote server you also need to rewrite Backbone.Marionette.TemplateCache.prototype.loadTemplate. Here example:
Marionette.TemplateCache.prototype.loadTemplate = function ( templateId ) {
var template = '',
templateUrl = 'path_to_your_template/' + templateId + '.html';
// Loading template synchronously.
Backbone.$.ajax( {
async : false,
url : templateUrl,
success : function ( templateHtml ) {
template = templateHtml;
}
} );
return template;
};

Related

AngularJs interval only shows {{name}}

I'm trying to get a list of all the 'cashflow' objects in my django application by calling a AngularJS get function every 5 seconds. I run the function with $interval(getCashflows, 5000); in my js file and try to display it in my html as [[getCashflows]] (see interpolateprovider)
Now the only thing I get is "[[getCashflows]]" in my html.. does interpolateProvider not work or do I need to call it differently?
app = angular.module("coco",[]);
app.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
});
app.controller('cocoCtrl',['$scope','$http', function($scope) {
$scope.save = function (cashflow) {
var dataObj = {
value : cashflow.value,
date : cashflow.date,
};
$.ajax({
url : "/create_cashflow/", // view functie
type : "POST",
data : dataObj,
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
success : function(json) {
$(".data").prepend("<li><strong>"+json.value+"</strong> - <em> "+json.date+"</em></li>");
}
});
}
}]);
app.controller('cocogetCtrl',['$scope','$http', function($scope,$http, $interval) {
$scope.cashflows = "";
$interval($scope.getCashflows = function() {
return $http.get("/get_cashflows/", {data:data}).then(function(response) {
$scope.cashflows = "test";
alert(response.toString());
$(".flows").prepend("<li><strong>"+json.value+"</strong> - <em> "+json.date+"</em></li>");
return response.toString();
});
}, 5000);
}]);
Your problem is almost certainly that you are attempting to update an angular scope variable from a jQuery callback. Angular only checks for changes to the scope inside its own digest loops and the callback will happen outside that context so angular doesn't see the change.
The simple fix is to stop using $.ajax() calls and start using the $http service that you have already included in your controller.
However it isn't at all clear what you expect to see in your html. The function getCashflows isn't returning a value, either as written or indeed if you rewrite it to use $http. The value is retrieved from the server asynchronously. You should change it so that the scope value is a promise which resolves to the expected value. As $http already returns a promise it should be sufficient to do something like:
function getCashflows() {
$scope.cashFlows = $http.get("/get_cashflows/", {data:data})
.then(function(response) {
return response.data;
});
}
The change your html to interpolate the value cashFlows instead of the function.
There is probably no reason for getCashflows itself to be exposed to the scope, you could just make it an ordinary function which will have the side effect of fixing the call to $interval that will currently just cause the javascript to stop due to an unresolved name.

How to access template instance data from autoform hook

I declare a instance related dictionary.
Template.newMessage.onCreated(function () {
var self = this;
self.dict = new ReactiveDict('namedDic');
});
And access it via Template.instance().dict.get() in helpers and events.
However, autoform nests another template somewhere so my code to access this dict, no longer works:
AutoForm.hooks({
serverCall: {
before: {
method: function (insert, update, current) {
insert.foo = Template.instance().dict.get('foo'); <-- fails
Meteor.call('serverCall', insert);
return true;
}
}
}
});
The doco doesn't seem to mention any way to access the parent template and I don't want to start guessing how many levels of parents it is.
If it makes any difference, my markup looks like this:
+quickForm id='newMessage' schema='Schema.CustomMessage' type='method' meteormethod='serverCall'
This is a bit of a A->B problem, what I want to do is just have autoform generate and validate a few fields, then I can insert some hidden values as part of a helper/event/hook before sending it off to the server.
I'm sure it is not a best approach, but I got it works by setting external variable in the same file scope.
Example:
var myNewMessageTemplate = null;
Template.newMessage.onCreated(function () {
var self = this;
self.dict = new ReactiveDict('namedDic');
myNewMessageTemplate = self;
});
AutoForm.hooks({
serverCall: {
before: {
method: function (insert, update, current) {
insert.foo = myNewMessageTemplate.dict.get('foo'); <-- fails
Meteor.call('serverCall', insert);
return true;
}
}
}
});
What you need to be insure, that hooks and onCreate are in the same file, to be in the same scope.
I've done this in the past like so:
Define a helper to retrieve the ReactiveDict
Template.newMessage.helpers({
foo : function () {
return Template.instance().dict.get('foo');
}
});
Then, I include that helper somewhere in my template. Use class="hidden" if you don't want the user to see it.
<input type="text" class="hidden" value="{{foo}}" data-schema-key="foo" name="foo">
When quickForm submits, I believe it looks for all inputs with a data-schema-key attribute defined (or possibly it looks at the name, you can define both just to be certain.
Using this method, you should not need to define any hooks.

How to compile inline HTMLBars templates?

I have an inline template (template in JavaScript) that I used to compile like so (where temp is a Handlebars string):
var template = Handlebars.compile(temp);
template({model: results}); // Gets the HTML
I'm trying to use HTMLBars instead, but can't quite figure it out. I did the following:
var template = Ember.HTMLBars.compile(temp);
template({model: results}); // Throws an error that template isn't a function
How do I go about getting the HTML back from the HTMLBars template. I also tried:
var template = Ember.HTMLBars.compile(temp);
Ember.HtmlBars.template(template, {model: results});
Which doesn't error, but also doesn't use the model when rendering the HTML. I think I'm close, but can't quite figure out how to inject the model.
HTMLBars doesn't produce functions like Handlebars did. Handlebars was a string templating language: you compile a string to a template function, then run that function to produce a string. HTMLBars compiles a string into a template but the template doesn't produce a string, it produces DOM nodes. The simple answer is that you're not going to be able to do the same things with HTMLBars as you did with Handlebars. I suggest either adding another string templating library to your code (maybe Handlebars), or letting a view handle the HTMLBars template as seen in this question.
And if you're curious, here's what an HTMLBars template object looks like after being compiled (gotten from a JSBin console dump):
[object Object] {
blockParams: 0,
build: function build(dom) {
var el0 = dom.createDocumentFragment();
var el1 = dom.createTextNode("");
dom.appendChild(el0, el1);
var el1 = dom.createTextNode("");
dom.appendChild(el0, el1);
return el0;
},
cachedFragment: null,
hasRendered: false,
isHTMLBars: true,
isMethod: false,
isTop: true,
render: function render(context, env, contextualElement) {
var dom = env.dom;
var hooks = env.hooks, content = hooks.content;
dom.detectNamespace(contextualElement);
var fragment;
if (env.useFragmentCache && dom.canClone) {
if (this.cachedFragment === null) {
fragment = this.build(dom);
if (this.hasRendered) {
this.cachedFragment = fragment;
} else {
this.hasRendered = true;
}
}
if (this.cachedFragment) {
fragment = dom.cloneNode(this.cachedFragment, true);
}
} else {
fragment = this.build(dom);
}
if (this.cachedFragment) { dom.repairClonedNode(fragment,[0,1]); }
var morph0 = dom.createMorphAt(fragment,0,1,contextualElement);
content(env, morph0, context, "foo");
return fragment;
}
}

Properly loading Template.templateName.rendered data context

I have a template, chartEditPreview, that runs a D3.js chart-drawing function once the rendered callback fires. It looks like this:
Template.chartEditPreview.rendered = function() {
drawChart(".chart-container", this.data);
}
Where .chart-container is the target div and this.data is the data for the object in the DB currently being accessed. Problem is, this.data often returns null, seemingly at random. It looks like this has something to do with how the publish/subscribe pattern works — Iron Router (which I'm using) lets the templates render and then hot-pushes the data into those templates.
My question is (hopefully) pretty simple: how can I make sure this.data is actually full of DB data before drawChart is run? Should I be doing this in some other way, instead of calling it on the rendered callback?
I'm thinking of storing the DB data in a Session variable during the routing and calling that from rendered, but it seems like an extra step, and I'm not certain it'll fix this problem. The chart's also not rendered only once on the page — it's interactive, so it needs to be redrawn every time the database object is updated via one of the inputs on screen.
Any help would be much appreciated. Thanks!
For reference, here's what my routes.js looks like:
Router.route('/chart/edit/:_id', {
name: 'chart.edit',
layoutTemplate: 'applicationLayout',
yieldRegions: {
'chartEditType': { to: 'type' },
'chartEditPreview': { to: 'preview' },
'chartEditOutput': { to: 'output' },
'chartEditAside': { to: 'aside' },
'chartEditEmbed': { to: 'embed' }
},
data: function () {
return Charts.findOne({_id: this.params._id});
},
waitOn: function () {
return Meteor.subscribe('chart', this.params._id);
}
});
And my publications.js:
Meteor.publish("chart", function (id) {
return Charts.find({ _id: id });
});
This is a common problem with Meteor. While the subscription might be ready (you should check for it like Ethaan shows) , that does not mean the find() function actually had time to return something.
Usually I solve it with some defensive code, i.e:
Template.chartEditPreview.rendered = function () {
if(this.data)
drawChart(".chart-container", this.data);
// else do nothing until the next deps change
}
Of course this is not as clean as it should be, but as far as I know the only way to solve problems like this properly.
Updated answer
In this case we need a dependency to trigger rerun on data change. Iron router solves this for us:
Template.chartEditPreview.rendered = function () {
var data = Router.current() && Router.current().data();
if(data)
drawChart(".chart-container", data);
// else do nothing until the next deps change
}
add this.ready() into the data:function
data: function () {
if(this.ready()){
return Charts.findOne({_id: this.params._id});
}else{
this.render('loading')
}
},
Something using data and waitOn could be a little bit tricky
Template.chartEditPreview.rendered = function() {
Meteor.setTimeout(function(){
drawChart(".chart-container", this.data);
},1000)
}

How to use JST with underscore.js?

I'm having troubles using variables that would normally be no problem with understand.js, but seemingly when you combine JST with underscore.js it seems to struggle.
var something= SD.defaultView.extend({
el: 'page',
template: JST['app/www/js/templates/sex.ejs'],
data: {
header: 'some information!!!',
image: '/img/path.jpg'
},
render: function () {
var compiled = _.template(this.template(), this.data); //I pass in the complied JST template
this.$el.html(compiled);
}
});
JST File rendered
this["JST"]["app/www/js/templates/sex.ejs"] = function (obj) {
obj || (obj = {});
var __t, __p = '', __e = _.escape;
with (obj) {
__p += ((__t = ( header )) == null ? '' : __t) + '<sexform>Hello There</sexform>';
}
return __p
};
Error
ReferenceError: header is not defined - templates.js (line 21)
...obj = {});var __t, __p = '', __e = _.escape;with (obj) {__p +=((__t = ( header )...
sex.ejs
<%= header %><sexform>Hello There</sexform>
Background Information
As expected, header is not available at the time of the reader, which is happening via grunt file with each change to my JST templates. I feel I must be implement JST's the wrong way.
But, to me this seems like the correct way of doing everything.
Of course, I am trying to use variables with underscore inside of sex.ejs
All of this code can be seen here: http://m.sexdiaries.co.uk/#wank
NB:
I can assure that this is safe for work and contains no images, even though as misleading as the url is its really not adult material, its an educational app.
You have this to define the view's template:
template: JST['app/www/js/templates/sex.ejs'],
And JST contains functions (which is, more or less, the whole point of using JST-style precompiled templates):
this["JST"]["app/www/js/templates/sex.ejs"] = function (obj) {
Then you do this:
var compiled = _.template(this.template(), this.data);
// function call ----------------------^^
Two things are wrong there:
You've already called _.template to compile the template.
this.template is the compiled template function that expects to be fed this.data.
The fix is quite simple:
var compiled = this.template(this.data);

Categories