Using the node.js framework Meteor -
how come the currentUser variable is defined in a template such as here:
<template name="main_template">
<div class="container">
{{#if currentUser}}
{{> add_player_form_template}}
{{/if}}
</div>
</template>
but when I call currentUser from the console, it's undefined:
however, Meteor.userId is defined:
why is this?
{{ currentUser}} is a helper in the main_template template.
In your client Javascript you'll need to define that helper method. Something like:
Template.main_template.helpers({
currentUser: function() {
return Meteor.userId();
}
})
This may help too http://docs.meteor.com/#/basic/templates.
{{ currentUser }} is a template helper that simply calls Meteor.user().
In the console, you need to call Meteor.user().
Related
I have an Ember route with a model that loads data from a few different places, using Ember.RSVP.hash. Each of these results in a call to a different API route in the backend:
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model() {
return Ember.RSVP.hash({
profile: this.store.queryRecord('profile', {}),
subscriptions: this.store.findAll('subscription'),
packages: this.store.findAll('package'),
});
},
});
The problem I'm having is that when any of the data calls throws an error, the entire template fails to load. What I would like to do instead is display as much data as is available even in case of an error, with the portions that couldn't be loaded displayed as an empty model of the appropriate type (with some additional error information). However, I don't seem to be able to do this. I tried adding an error handler to the route, but from the error handler there doesn't seem to be any way to continue the transition despite the error.
One possibility is to use service for obtaining such information. Model will be completely loaded but data from service will show after they will be loaded.
Take a look at http://emberigniter.com/render-promise-before-it-resolves/
I would suggest passing data down from the route, whether it uses one or more other services to retrieve the data (also from the same author http://emberigniter.com/should-components-load-data/ ). The template can be rendered partially by populating the model provided to the components with separate requests. A good place for this kind of code is in setupController. Usually I only add a call to a service from a component if there is a logical coupling between the two eg a component showing a specific ad.
rough example,
http://emberjs.jsbin.com/netesehasi/1/edit?html,js,output
js
...
App.IndexRoute = Ember.Route.extend({
model:function(){
return {dataForA:null,dataForB:null,dataForC:null};
},
setupController:function(controller,model) {
this._super.apply(arguments);
// simulate fetching data
setTimeout(function(){
Ember.set(model,'dataForA', 'this is data for component a');
controller.set('model', model);
},2000);
setTimeout(function(){
Ember.set(model,'dataForB', 'this is data for component b');
controller.set('model', model);
},1000);
setTimeout(function(){
Ember.set(model,'dataForC', {error:'error for component c'});
controller.set('model', model);
},3000);
}
});
...
hbs
...
<script type="text/x-handlebars" data-template-name="components/comp-a">
{{#if data}}
{{data}}
{{else}}
loading...
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="components/comp-b">
{{#if data}}
{{data}}
{{else}}
loading...
{{/if}}
</script>
<script type="text/x-handlebars" data-template-name="components/comp-c">
{{#if data}}
{{#if data.error}}
{{data.error}}
{{else}}
{{data}}
{{/if}}
{{else}}
loading...
{{/if}}
</script>
...
I have created an Admin area on my website, which is protected by a login, that is powered by the account-package.
My Admin Template currently looks like this:
<template name = "Admin">
{{#if currentUser}}
{{> SideNav}}
{{> Template.dynamic template = getCurrent}}
{{else}}
{{> Login}}
{{/if}}
</template>
It works, but when i change the website, it always shows the login page for a second, before it changes to the dynamic template. It is short, but you can notice it and it doenst look very nice. So how should i go about this? I am not sure how to fix this.
Bringing your logging-in logic on the view might be an easy way to do, but as you can see it, it is not worth.
The logging-in related tests must be done asap in your application. You should do it in the router, as it will allow you to efficiently manage the accesses and start subscriptions before the view start rendering (this last point depends on your package and yout way of managing the renders though).
In addition, several packages provides very relevant tools to improve your app perfs and rendering in this kind of situation.
Here are some of them :
meteorhacks:fastRender
meteorhacks:subscription-manager
kadira:flow-router (rather than Iron:router, which is way more random in rerunning the route and renders.)
Here is some example of how you would handle it with Flow Router.
The following example architecture is built according to The Meteor Chef model.
In this example, I assume you use alaning:roles package and code according to last version of Ecmascript.
/both/routes/__triggers.js
// Let's declare some namespace for our routing triggers
Triggers = {};
Triggers.mustBe = {};
Triggers.mustNotBe = {};
// Here, we check for the state of the client
Triggers.mustBe.loggedIn = ( ) => {
if (!(Meteor.loggingIn() || Meteor.userId()))
// If he is not logged in or logging in, we handle the redirection
{
FlowRoute = FlowRouter.current();
if (FlowRoute.route.name != "home")
Session.set("redirectAfterLogin", FlowRoute.path);
FlowRouter.go('/splash');
}
};
Triggers.mustBe.admin = ( ) => {
// Same here. If the user is not an admin, we should redirect him somewhere or prevent the routing to be executed
if (!Roles.userIsInRole(Meteor.user(), ['admin']))
FlowRouter.go(FlowRouter.current().path);
};
// Just an example of what if would looks like if we wanted to be sure the client is not connected (for accessing the login screen for example)
Triggers.mustNotBe.loggedIn = ( ) => {
if (Meteor.loggingIn() || Meteor.userId())
FlowRouter.go('/');
};
/both/routes/_configuration.js
// We set the rendering root to the 'body' tag. Check for the doc links I give below
if (Meteor.isClient) Meteor.startup(function() { BlazeLayout.setRoot('body'); });
exposed_Routes = FlowRouter.group({
name: "exposed",
triggersEnter: []
});
loggedIn_Routes = FlowRouter.group({
name: "loggedIn",
triggersEnter: [
Triggers.mustBe.loggedIn
]
});
// You might see that we declare the admin routes group from the logged_in group. Doing so, we will execute all the logged_in triggers before executing the one we define here. It will allow us to check if the user is connected before checking he is an admin
admin_Routes = loggedIn_Routes.group({
name: "admin",
triggersEnter: [
Triggers.mustBe.admin
]
});
/both/routes/admin.js
admin_Routes.route('/admin/reports', {
name: "reports",
action: function (params, queryParams) {
// We use kadira:BlazeLayout package to manage the rendering
BlazeLayout.render('adminLayout', { main: "someTemplate", menu: "SideNav" });
// Any other logic you would execute each time you create this route.
}
});
/client/templates/layouts/admin.html
<template name="adminLayout">
{{> Template.dynamic template=menu }}
{{> Template.dynamic template=main }}
</template>
BlazeLayout & FlowRouter docs (by Kadira)
Probably you need {{loggingIn}} helper which is reactive and is true while login method is currently in progress.
http://docs.meteor.com/#/full/template_loggingin
<template name = "Admin">
{{#if loggingIn}}
Loading...
{{else}}
{{#if currentUser}}
{{> SideNav}}
{{> Template.dynamic template = getCurrent}}
{{else}}
{{> Login}}
{{/if}}
{{/if}}
</template>
How do I use a template-helper to edit the value of the parameter I passed into a route created using the pathFor method of iron-router???
I have this template-helper:
Template.registerHelper('slugify', function(obj){
return _.slugify(obj);
});
in my .html file I have this:
{{#each menuItemsFromDB}}
{{#each arrayOfMenuItems}}
<a class="item category" href="">
{{this}}
</a>
{{/each}}
{{/each}}
The {{this}} in the above code returns a string, the name of the category.
Because I have registered a template helper, I can SLUGIFY this category by:
{{slugify this}}
and then I have this route:
this.route('List',{
path: '/list/:category_slug',
template: 'list',
controller: 'ListController'
});
I can link to this route and pass a parameter to this route by:
{{pathFor 'List' category_slug='the-value-i-passed'}}
But that would be hard-coding it which cannot achieve the desired result I want.
I want it to be dynamic by using the 'slugify' template helper and iron-router's pathFor method and using the value of {{this}}.
What I'm trying to achieve is something like this although this code below doesn't work:
{{pathFor 'List' category_slug={{slugify this}} }}
What's the work around to achieve what I'm 'trying' to achieve with the above line????
I was hoping I can do something like:
{{pathFor 'List' category_slug=slugify(this) }}
or
{{pathFor 'List' category_slug='{{slugify this}}' }}
Long story short, what you're looking for is not yet implemented using the current syntax, although it's part of the Handlebars standard implementation, upon which Meteor Spacebars is based.
For the moment, you have to create a separate helper that slugifies your input and call it within pathFor.
JS
Template.myTemplate.helpers({
slugified: function(){
return _.slugify(this);
}
});
Spacebars
{{pathFor 'List' category_slug=slugified}}
Note that Handlebars sub-expression support is planned in a near future and might even make its way to the next version of Meteor according to this PR : https://github.com/meteor/meteor/pull/4101
I have a handlebar statement which passes a userid to a helper. I am unsure how this works. The handlebar {{#if isowner ..}} has .. what is passed here as parameter to the helper funktion?
<template name="test">
...
<table class="table table-hover table-striped">
{{#each tester}}
<tr><
<td>{{#if isowner ..}}
<i class="fa fa-trash removeUser"></i>
{{/if}}
</td>
</tr>
{{/each}}
</table>
...
</template>
Template.test.helpers({
'isowner':function(parent){
return parent.userId === Meteor.userId();
}
});
Obviously this is only true when the userid's are identical. Meteor.userId() is the current user on the client side. So which userid is passed to parent?
Sure, the name speaks for its self. It must be one level above - but what is this technically? Where does .. go?
.. returns the data context of the parent (enclosing) template or structure -- I rigged up a very simple MeteorPad here that you can maybe play around with to see how it works.
In your case, I think it's probably returning the data context for the test template. You can console.log(parent) in your helper to inspect that object and get a bit more info:
Template.test.helpers({
'isowner':function(parent){
console.log(parent);
return parent.userId === Meteor.userId();
}
});
You can find more information about how the .. is resolved and how it can be used in the spacebars readme.
In meteor I can set various template helpers like this:
Template.story.title = function () {
return "title";
};
<template name="story">
<h3>{{title}}</h3>
<p>{{description}}</p>
</template>
Which is great but, if I have a lot of variables I wouldn't want to set them individually, I want to pass the context to the main template.
How do I do that?
Template.story.data = function () {
return {title:"title", description:"desc"};
};
<template name="story">
<h3>{{title}}</h3>
<p>{{description}}</p>
</template>
That doesn't work. THanks
You can set the context of the template when you call it:
{{> story data}}
Template.outerTemplate.data = function() {
return {title:"title", description:"desc"};
}
Or you can just use {{#with}} to set the the template context on the fly:
{{#with data}}
{{title}}
{{/with}}
You are absolutely on the right way but missed to use your template variable the way you defined it. As Template.story.data is defined to return an object, you should use it like an object:
<template name="story">
<h3>{{data.title}}</h3>
<p>{{data.description}}</p>
</template>
Voilá. Of course every template variable can hold more than just a string.