Every example that I have seen with iron-router specifies the template name with a string. Is it possible to do this with a variable? Suppose you have several routes that all use the same dynamic path, and the same data function, but they all need different templates. Is there a way to do this without specifying a different route for every template (which would also mean changing the path I use)?
You can programmatically specify things like the template and the layout with a custom action function. The example below demonstrates showing a particular template if the required document is found based on an id in the route. You can use the same semantics for both routes and controllers.
var postsController = RouteController.extend({
waitOn: function() {
return Meteor.subscribe('post', this.params._id);
},
action: function() {
if (Posts.findOne(this.params._id)) {
this.layout('postsLayout');
this.render('posts');
} else {
this.render('notFound');
}
}
});
Once 0.8.2 is released it should be trivial to do with UI.dynamic even without Iron-Router: https://github.com/meteor/meteor/issues/2123.
Related
I am a beginner with AngularJS and I have a little problem, I installed grunt-contrib-less to support less files instead css but now I have to declare all less styles that will be compiled into only one css file.
But my problem is normally when I'm using less, I write some code for a specific page, and here I have to write the style code for all pages. This is confusing and not really maintanable so is there a best practice to organize less styles?
I tought that there may be multiple solution:
Apply a class to body tag and change it with I don't know what
(controller, services or other)
(Import LESS file only for one page)
Generate multiple css file depending which style is compiled (but I can't do this because I can't configure grunt correctly)
Do this with DOM manipulation (but it don't find it beautifull because I think Angular must have a good way to solve that problem)
Could you explain me how to have different style for differents views ? I don't want to have the same style for all links in all views and without create hundreds classes I don't know how to do that.
Use directive
and add whatever variables/code/logic you want to add
HTML template(directive) of style can be added to your view and after compile you will get different ui for all your views
for reference read
angular directive
I solve my problem by adding specific class on body tag depending the route.
I put a variable in rootScope called 'pageStyle' with correspond to the classname that I want. This variable is updated automatically when route change (see run function). There is an event when the route change ($stateChangeSuccess or $routeChangeSuccess depending if you are using ngRoute or -angularui-routeur).
In my case i would like to add the name of the route but you can do it with the controller name or something else.
Here is an example
This is the routes :
angular
.module('frontApp', [])
.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider, $mdThemingProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: '../views/home.html',
controller: function ($scope) {
$scope.msg = 'Xavier';
}
})
.state('form', {
url: '/form',
templateUrl: '../views/form.html',
controller: 'FormCtrl'
});
}])
And in the run function you will see the event bound to adapt the class when route change :
.run(function($rootScope) {
$rootScope.pageStyle = '';
// Watch state and set controller name in pageStyle variable when state change
$rootScope.$on('$stateChangeSuccess', function(event, toState) {
event.preventDefault();
if (toState && toState.name && typeof toState.name === 'string'){
$rootScope.pageStyle = toState.name;
} else {
$rootScope.pageStyle = '';
}
});
});
Extra informations :
Note that the event called when route change is different if you are using ngroute. use "$routeChangeSuccess" if you use ngRoute and "$stateChangeSuccess" if you choose to use angular-ui-routeur
If you want to add the controller name instead the route name simply use the follow and replace 'ctrl' with you controller suffixe:
if (toState && toState.controller && typeof toState.controller !== 'function'){
$rootScope.pageStyle = toState.controller.toLowerCase().replace('ctrl','');
}
Hope it help someone else
I have a template named profile which contains three other templates. One of these templates is {{> postlist}}
and the helper function for this template is
Template.postlist.helpers({
posts: function() {
return Posts.find({rph: {$in : postsArr}});
}
});
The problem is on going to the route, postlist template is empty, since postsArr is calculated later after the dom has loaded on the basis of other two templates. But, if I click on other route and come back to this route, the template renders properly.
What should I do that template renders properly initially itself?
The easiest way would be to us Session, though it's probably the worst option:
Template.postlist.helpers({
posts: function() {
return Posts.find({rph: {$in : Session.get('postsArr') }});
}
});
If you now call Session.set('postArr', ...) anywhere in your code the posts helper will update automatically. The second option is to use a shared reactive variable:
var postsArr = new ReactiveVar();
and then inside your helper:
return Posts.find({rph: {$in : posts.Arr.get() }});
Now you can do postsArr.set(...) and everything should work fine. Just remember to meteor add reactive-var do your project.
One last doubt is: where to put that reactive variable declaration? In most cases you can do away with putting in a single "controller" file. It will work as long as:
- you only have one instance of your template a time
- the code which sets ad gets the value of you reactive variable may be put in the same file
If one of the above conditions does not hold, then the only option to go, which is BTW the best possible, is to put your state variable in your template's scope. This is how you do it:
Template.postsList.created = function () {
this.postsArr = new ReactiveVar();
};
Template.postlist.helpers({
posts: function() {
return Posts.find({rph: {$in : Template.instance().postsArr.get() }});
}
});
From helpers you can always access postsArr using the Template.instance() routine which always return the current template instance, for which the helper was called. From event handlers, note that the second argument of your handler is always the template instance, which you're interested in.
If you need to access it from another templates, then you should probably put your state variable on the corresponding route controller. Assuming you're using iron-router, that would be:
Iron.controller().state.get('postsArr');
The Iron.controller routine grants you access to the current route controller. Read this for more details.
I have a template with few elements (input, radioButton etc). If I want to access to these DOM elements within mytemplate I can either access them within events
Template.myForm.events({
'click #submitButton' : function (event, template) {
//template variable here gives me access to the
//current template instance, so I can get to any
//DOM element within this template.
}
})
OR within
Template.myForm.rendered = function () {
//within this function I have access to "this" which points to template instance
}
I was wondering if there is a way to access the DOM Elements that a declared within a template outside of these event functions and rendered callback?
Thanks in advance
You can but you need to reference the template instance.
The reason for this is a single template can be used multiple times. In this case a single easy to use way to access the template would not know which instance it would belong to. This is why you need to use a reference, such as done in the example below.
You have to store the instance somewhere when it is rendered:
TheTemplateInstance = null;
Template.myForm.rendered = function() {
TheTemplateInstance = this;
}
Then you can use TheTemplateInstance anywhere you want, provided the template is on the DOM.
If you use myForm many times then it will only have access to the one created last.
Also You did not give a use case for your intentions. But there are several better ways to do most things with a template:
JQuery modding something when some variable changes (the most common use case where helpers aren't useful)
Template.myForm.rendered = function() {
var self = this;
this.autorun(function() {
var xx = something.findOne();
self.$("something").autoform() //Some jquery call
});
}
and helpers:
Template.myForm.helpers({
someName: function() {
return Session.get("name");
}
});
You can then use {{someName}} in your template's html where it can change when you use Session.set("name", "a new value");
I am forced to assign rendered callbacks to all my templates.
Until 0.9.0 I used to do it like this:
_.each( Template, function( template, name ) {
//...
template.rendered = function() {
//...
};
});
But now, Template is a constructor and not an object, so this method won't work here. Is there any way to pass callback function to all templates or fire function when all templates were rendered using Blaze?
Here is a quick workaround I came up with, iterating over every Template property to find out if it corresponds to a template definition, and if it does, assign the onRendered callback.
// make sure this code is executed after all your templates have been defined
Meteor.startup(function(){
for(var property in Template){
// check if the property is actually a blaze template
if(Blaze.isTemplate(Template[property])){
var template=Template[property];
// assign the template an onRendered callback who simply prints the view name
template.onRendered(function(){
console.log(this.view.name);
});
}
}
});
I don't know what's your use case so there may be better solutions depending on it.
With Meteor 1.2.1 the Template object has an onRendered(hook) function to accomplish an 'all template' onRendered behaviour.
Template.onRendered(function(){
var template = this;
Deps.afterFlush(function() {
console.log("triggering Jquery mobile component creation for "+template.view.name);
$(template.firstNode.parentElement).trigger("create");
});
});
The postponed update via Deps.afterFlush(callback) is optional and subject to your application needs.
I'm working on a survey/wizard type of meteor application.
The interface will only have two navigation buttons, who's caption/value will be determined dynamically based on whatever step a given user is currently at.
My question is: what markup syntax should I use within each of these two button's definitions in order to direct the Iron Router to change the route. Another words, where in the button definition should I put this: this.redirect('/anotherpath') ?
If you have a button in your template. If you are using a hrefs check answer from f3rland.
html:
<button #id="mybutton>m<Button</button>
you can catch the buttonpress event with javascript
js
Template.myTemplate.events({
'click #myButton': function(event, template) {
event.preventDefault();
Router.go('anotherpath');
}
});
The router should look like:
Router.map(function() {
this.route('anotherpath', {
path: '/',
layoutTemplate: 'myLayout',
controller: myController
});
});
I suggest you to use Iron-Router pathFor helper in your template
{{buttonText}}
That will redirect the user to the corresponding page/template.
You should also take a look at new blaze pattern to define custom block helper
Edit:
You could also use a custom helper for dynamic route as mentioned here
UI.registerHelper("_foo", function fooHelper() {
var templateName = figureOutNameDynamically(this.context.criticalInfo);
return Template[templateName];
});
<template name="foo">
{{> _foo context=.. atts=this}}
</template>
you can do this/change the route by using the action option which will automatically cal this.render().
you can do it yourself by calling the action function but I guess it is better to use onBeforeAction/after hooks