Find all routes defined in Iron Router - javascript

Is there a way to add a site map dynamically for all paths which are defined in the Iron Router Router?
I'm thinking of something like this:
<ul>
{{#each paths}}
<li>{{route}}</li>
{{/each}}
</ul>
Also, maybe corresponding sub-paths which will be displayed in a child ul?
Obviously I could also just create the list manually, but for an app with 50+ links this is quite of a work.

Certainly! As Kassym noted, Router.routes contains a list of all the routes. However it contains the router functions, so you'll have to go and grab their names with route.getName(). The default route doesn't have a name, so you'll have to grab the .path() instead.
The whole thing should look like this in your helper:
Template.allRoutes.helpers({
paths: function () {
var allRoutes = _.map(Router.routes, function(route){
var routeName = typeof route.getName() === 'undefined' ?
route.path() :
route.getName();
return {route: routeName}
});
return allRoutes
}
});
And this in your template:
<template name="allRoutes">
{{#each paths}}
<li>{{route}}</li>
{{/each}}
</template>
Working Demo in MeteorPad
Note: Remember to enclose pathFor in curly brackets because it is a helper method. It will execute javascript and inherit the current datacontext, so you can pass it any property from the current context.
In order to display an sub paths of n depth, you can recursively call your template like this:
<template name="subpaths">
<ul>
{{#each subpaths}}
<li>
{{path}}
{{#if subpaths}} {{>subpaths}} {{/if}}
</li>
{{/each}}
</ul>
</template>
Demo in meteor pad
For more info, see getting the names of all routes for the accounts-entry package

Related

Show certain elements when using common template based on url in meteor

I am using on a common template in two pages. I have to show some lines in one page. So i have added an if function which will check the current url . It is working for me only once. After coming back to the same url i am getting previous url's value. Then this it is not changing . Below is a sample code. How to achieve this?
Below is my code
Url- /template1
Template1.html
<template name="template1">
{{>commontemplate}}
</template>
Url - /template2
Tempalate2.html
<template name="tempalte2">
{{>commonTemplate}}
</template>
CommonTemplate.html
<template name="commonTemplate">
{{#if isAdmin 'template1'}}
<div> hello</div>
{{else}}
<div> hai</div>
{{/if}}
</template>
CommonTemplate.js
Template.CommonTemplate.helpers({
isAdmin: function (val) {
var path = window.location.pathname;
var str = path.split("/");
return val === str[1];
}
})
The reason it's not rerunning is that your helper function has no reactive dependencies. Normal Javascript variables (like window.location.pathname) don't instruct reactive computations (like helper functions) to rerun when their values change.
The two easiest possibilities are:
Name your routes and use FlowRouter.getRouteName() in the helper function, which is reactive.
Add the line FlowRouter.watchPathChange() to your helper, which doesn't return anything, but does ensure the containing reactive function reruns whenever the path changes. FlowRouter API.
The other alternative is to simply use CSS. Have a look at meteor-london:body-class, which appends the route name as a class to the body, allowing you to selectively show or hide stuff based on route in your CSS.

How Do I Use a Meteor Template Helper to Edit a Value Passed as a Parameter in Iron-Router?

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

In Meteor using #each, check if 'last' element in the collection reached

I'm iterating through a collection in Meteor using {{#each}} and I would like to know if I'm in the last element, as I can do in AngularJS while using ngRepeat with $last.
It could be used, for example to construct human readable enumerations like 'I like cats, dogs and dolphins' :
Template.myTemplate.helpers({
likedAnimals: function(){return ['dogs','cats','dolphins'];}
});
<template name='myTemplate'>
I like
{{#each likedAnimals}}
{{#if !$first && !$last}}, {{/if}}
{{#if $last}} and {{/if}}
{{this}}
{{/each}}
</template>
Is there any way to check this condition in Meteor?
If any of you are wondering how to do the same with collection cursors, there's a much simpler way thanks to handlebar-helpers package.
You could then use:
$mapped - will map $first, $last, and $index onto your cursor or array
combined with $last helper in your template like that:
{{#each $mapped myCursor}}
{{name}}{{#unless $last}},{{/unless}}
{{/each}}
PS: this also works with arrays
Using underscore.js :
Template.registerHelper('last',
function(list, elem) {
return _.last(list) === elem;
}
);
<template name='myTemplate'>
{{#each likedAnimals}}
{{#if last ../likedAnimals this}} I'm the last ! {{/if}}
{{/each}}
</template>
Worked with a reactive data source for me with meteor 1.1.0.1 (I don't know when Template.parentData() was introduced in meteor).
This isn't supported in meteor yet (version 1.0), but you can kind of add it yourself by doing something like this:
Template.myTemplate.helpers({
likedAnimals: function(){
var animals = ['dogs','cats','dolphins']
return animals.map(function(animal, index){
return {
name: animal,
isFirst: index==0,
isLast: index==animals.length-1
}
})
}
})
However, this does not play nice with reactivity (making it work properly with reactivity is much harder, which I guess is the reason why this isn't a built in feature yet), but if you return a simple array that's not dependent on any reactive data source, this should work fine.

How can I get an instance of a model's controller inside a handlebars template?

I've got a sticky situation that I keep on running into: The need for a new instance of a controller inside a handlebars template.
Here is a brief example of my situation. (Please excuse my use of coffeecript)
In Ember, I have a model:
App.Foo = DS.Model.extend
attr: DS.attr()
...
Which I load from an endpoint etc.. And place into an array controller:
App.FooArray = Ember.ArrayController.extend
###*
* Array of App.Foo
* #type {Array}
*/
content:
method: ->
...
Finally, I have an 'instance' controller for this model, which implements further methods (i.e. this is not a singleton controller as would be found at the router level, but a decorator (or proxy) that augments the model with added methods and event handlers):
App.FooController = Ember.ObjectController.extend
###*
* Content
* #type {App.Foo}
*/
content: null
action: ->
...
In handlebars, I want to iterate over items in an App.FooArray:
{{#each myFooArray}}
Hi! My attr is {{attr}}
{{/each}}
etc.. This works splendidly for parameters and such.
However, the trouble starts when I want to use actions (or other properties which would belong to a FooController)
{{#each myFooArray}}
Hi! My attr is {{attr}} <a {{action 'action'}}>Action me!</a>
{{/each}}
Suddenly my actions are not working. That's because the action helper doesn't apply the action to 'this' but rather to a controller higher up, possibly even at the Route level!
So to work around this, I need to pass a target (i.e. a controller):
{{action 'action' target=**********}}
Well, the controller I want is an instance of App.FooController.
Up until now, I've been instantiating controllers inside the model (yuck!):
App.Foo = DS.Model.extend
attr: DS.attr()
...
attrn: DS.attr()
myController: Ember.computed (->
App.FooController.create
content: this
)
and thus iterating as follows:
{{#each myFooArray}}
Hi! My attr is {{attr}} <a {{action 'action' target=myController}}>Action me!</a>
{{/each}}
I know this is bad, but I can't think of a better way. Somebody, please help me see the light!
You can explicitly set the itemController in your each loop.
{{#each myFooArray itemController="foo"}}
Hi! My attr is {{attr}}
{{/each}}
This question poses an important and longstanding question about ArrayControllers, CollectionViews, Models and ObjectControllers.
At the time of writing, my knowledge of the inner workings of Ember was limited. However, I can rephrase my question more concisely as follows:
Given an ArrayController, CollectionView and instance controllers for a model, how can one leverage the itemControllerClass property of the ArrayController to iterate over its content and wrap each item in a unique (i.e. non-singleton) instance of itemController?
Turns out this problem is longstanding and the solution echoes #jeremy-green but I will expand on things here.
First off though: the Ember support thread that encapsulates the problem: https://github.com/emberjs/ember.js/issues/1637
I think the discussion there points very clearly to the need for non-singleton controllers in certain situations.
As well, here is the documentation for ArrayController that indicates the presence of an 'itemController' property on the ArrayController: http://emberjs.com/api/classes/Ember.ArrayController.html#property_itemController
Looking further into the docs, you will also note the presence of an 'lookupItemController' function: http://emberjs.com/api/classes/Ember.ArrayController.html#method_lookupItemController
These functions are for the express purpose of returning the content as an array of Controllers but how?
Well the first requirement is to use the ArrayController directly as the content in a loop. Unfortunately this is where things start to fall apart.
You may think it would be simply the case that a CollectionView can be used:
{{view myCollectionView controllerBinding=myArrayController}}
or
{{view myCollectionView contentBinding=myArrayController}}
But unfortunately this is not the case. More so in the situation where you are rendering a Controller on another Controller's route. CollectionView does not preserve the relationship between the ArrayController and its 'itemControllerClass`
The only way to make this work, as #jeremy-green points out:
{{#each myFooArray itemController="foo"}}
Hi! My attr is {{attr}}
{{/each}}
A more complete example would be:
<ol class="foo-item-list">
{{#each controllers.foo_items}}
<li>{{ view "foo" }}</li>
{{/each}}
</ol>
Wherein App.FooItemsController has either the property itemController or lookupItemController defined.
Unfortunately in this situation we lose the benefits of using the tagName or emptyView properties of CollectionView. Hopefully if an 'ArrayView' is ever created, it will bring the best of both worlds to this situation!

Handlebars.js - Global Contexts

Say I have a static list of users cached somewhere in my application like App.Users. I'll probably have the need to list my users in several dozen places in my application. Conventionally, I'll just need to pass my list in with my context to the template.
var tmpl = Handlebars.templates['TemplateName'];
var html = tmpl({
model: model,
users: App.Users
});
But this approach requires some wiring in both the template and the javascript. What I would like to do is specify this in the template alone so I don't need to remember this in my scripts. Consider something like this...
{{#each {{users}}}}
<li> ... </li>
{{/each}}
...Where users is a helper function that just returns my App.Users. Wouldn't that be nice?
So that totally doesn't compile. What is another solution?
Went with an abstract helper function deal... which let's be honest, seems to be the solution to 99% of Handlebars questions.
Handlebars.registerHelper('global', function(context, options) {
return options.fn(App.[context].toJSON()); // Object is Backbone Collection
})
And used in an example...
{{#global "Users"}}
{{#each this}}
<th>{{Name}}</th>
{{/each}}
{{/global}}

Categories

Resources