Can I use querySelector method in Blaze Template Instance - meteor

I know the way that lookup DOM with jquery.
Template.foo.events({
'click #bar'(eve, instance) {
$('selector'); //A common way.
instance.$('selector'); // Better performance.
document.querySelector('selector'); // its good too
instance.querySelector('selector'); // its not work
}
})
I know that instance is not node type, so naturally instance.querySelector('selector') can't work!
But I want to use like instance.$('selector').
Is there a way?

instance.find('selector') --> querySelector
instance.findAll('selector') --> querySelectorAll
Note that these both use jQuery under the hood.
If you really want a true browser queryselector without a wrapper use:
instance.firstNode.querySelector('selector')

Related

Adding components to existing Ractive instance

I have a somewhat weird question.
In Ractive we can do something like this.
App’s template:
<h1>My app</h1><SubComponent/>
And generally have SubComponent’s template access data from App.
Is there any way to have the same behaviour without mentioning SubComponent in the template?
Something like:
const App = new Ractive({el: ‘#myapp’, …………});
const SubComponent = Ractive.extend({ append: true, ………… });
const example = new SubComponent()
example.render(App.el)
But, with example’s template being able to access App’s data, maybe also giving App the chance to find it by using App.findComponent().
What I’m trying to do, is having unpredictable (= I can’t include them inside templates by default) nested components.
Hopefully this makes sense.
Any idea on how to do it?
I don't know if this will work out for you..
But you can just put a div with a known ID in
<h1>My app</h1><div id="subcomp" />
Then mount your dynamic SubComponent under there.
You can use can event like oncomplete - which will guarentee than div#subcomp has been added to dom.
oncomplete: function() {
this.mycompRef = new SubComponent( { el: '#subcomp' });
// You can skip findComponent and use mycompRef
}

is passing ractive as an argument good practice?

Let's say, instead of packing your onInit and onRender sections of your Ractive declaration in your index.html with a lot of functions that need access to the Ractive object, you wish to keep the index.html cleaner and simpler and reference functions in libraries in other files.
Is there any harm in passing Ractive itself as an argument, so these "external" functions can access it?
For example, instead of:
oninit: function() {
// tons of code here
}
doing this?
oninit: function() {
doThisThing(ractive)
}
Then, in a separate file:
function doThisThing(ractive) {
pingAnAPI(function(response) {
ractive.set('data', response);
)};
}
Just wondering if there would be memory issues or any other undesirable effect if you did this a lot.
Thanks, Ractive is awesome!
doThisThing appears to be your data layer, and should not be aware of your UI layer. Otherwise, you'd risk tightly coupled code.
Instead of breaking out the code and passing the ractive around, break away your data fetching logic. Call the API, have it return a promise. Ractive can hold on to that promise.
// On the component
oninit: function() {
var instance = this;
pingAnAPI().then(function(response){
instance.set('data', response);
});
}
// On your data layer
pingAnAPI: function(){
return $.get('path/to/endpoint');
}
Another way you can do it, since you're considering separate files, is to break out from index.html and use component files. Read the component spec for authors page for more details.
As for memory issues, I'd not worry about that as early as now. Code maintainability should be your first agenda.

How to access child context in parent templates?

If child template defined something in onCreated,
Template.test.onCreated(function() {
this.xxx = 'test';
});
And I want to access child context in parent. How can I do that?
You can use Session or ReactiveVar for this operation. But if you need to save it on the child template I think you could use:
var childView = Blaze.getView(currentTemplate.find('#your-child-theme-id'));
and then get template instance by:
var childTemplateInstance = childView._templateInstance;
and then
var myXXXvar = childTemplateInstance.xxx;
But this is a little bit dirty :/ I don't think there is a Meteor API for this particular use case. I think you should play with reactive vars or client only collections etc. I don't know exactly what you want to achieve.

How to debug template in Meteor/handlebars?

According to this blog post, I should register a helper to better debug handlebars templates, but is not working:
ReferenceError: Handlebars is not defined
So, how can I {{debug}} in Meteor/handlebars?
This is the helper function I use for debugging in my own projects:
Template.registerHelper("debug", function(optionalValue) {
console.log("Current Context");
console.log("====================");
console.log(this);
if (optionalValue) {
console.log("Value");
console.log("====================");
console.log(optionalValue);
}
});
You can then call it in your templates with {{debug}} and it displays the context you are currently in. Read more at http://docs.meteor.com/#/full/template_registerhelper.
In Meteor 0.4.0 you register handlers like this:
Template.myTemplate.helpers({
helper: function () {
// some code here
console.log(arguments);
}
});
There is no need to call Handlebars directly.
Make sure that you register your helper in client (or shared) meteor code.
Handlebars.registerHelper('helper', function() {
// Do stuff
});
This should be callable via {{helper}} in your templates.
For the sake of completeness: you can also use
Template.registerHelper('helper', helperFunc);
instead of Handlebars.regsterHelper('h',f);
A small reason this is better is that then your app won't need that much refactoring if you decide somewhere down the road that you want to use something else instead of Handlebars(i.e. Spacebars, the real name of meteors adaption) like jade for meteor.
This is really a comment to the accepted answer. Looking forward to one day hit 50 rep.

Run JS after rendering a meteor template

I have a template that looks something like this:
<template name="foo">
<textarea name="text">{{contents}}</textarea>
</template>
I render it with:
Template.foo = function() {
return Foos.find();
}
And I have some event handlers:
Template.foo.events = {
'blur textarea': blurHandler
}
What I want to do is set the rows attribute of the textarea depending on the size of its contents. I realize that I could write a Handlebars helper, but it wouldn't have access to the DOM element being rendered, which would force me to do some unnecessary duplication. What I want, ideally, is for meteor to trigger an event after an element is rendered. Something like:
Template.foo.events = {
'render textarea': sizeTextarea
}
Is this possible?
As of Meteor 0.4.0 it is possible to check if a template has finished rendering, see http://docs.meteor.com/#template_rendered
If I understand your question correctly, you should wrap your textarea resize code inside a Template.foo.onRendered function:
Template.foo.onRendered(function () {
this.attach_textarea();
})
I think the current 'best' way to do this (it's a bit of a hack) is to use Meteor.defer ala Callback after the DOM was updated in Meteor.js.
Geoff is one of the meteor devs, so his word is gospel :)
So in your case, you could do something like:
<textarea id="{{attach_textarea}}">....</textarea>
and
Template.foo.attach_textarea = function() {
if (!this.uuid) this.uuid = Meteor.uuid();
Meteor.defer(function() {
$('#' + this.uuid).whatever();
});
return this.uuid;
}
EDIT
Note, that as 0.4.0, you can do this in a much ad-hoc way (as pointed out by Sander):
Template.foo.rendered = function() {
$(this.find('textarea')).whatever();
}
Since about June 2014, the correct way to do this has been to set a callback using Template.myTemplate.onRendered() .
Yeah I think so - not sure if it's "the right way", but this works for me:
In your app JS, the client section will run whatever javascript there on the client. For example:
if (Meteor.is_client) {
$(function() {
$('textarea').attr('rows' , 12) // or whatever you need to do
})
...
Note the example here uses JQuery, in which case you need to provide this to the client (I think :-). In my case:
I created a /client dir, and added jquery.js file under this.
Hope this helps.

Resources