From this discussion here,
Allow a passed in variable to {{renderPages}} helper
I am trying to display a template within an existing {{renderPage}};
My Use case: I have a very simple high level body template
<template name="body">
{{> header}}
{{> notifications}}
<div class="content" id="content">
<div id="main-content">
{{{renderPage}}}
</div>
</div>
{{> footer}}
</template>
And as you can see, my main-content has the {{renderPage}}. This works great, when i set up a route:
'/home': 'home'
The route finds the template 'home' and replaces renderPage with that template. I want to expand that now and see if route templates can be placed within specific divs.
For example, in my home.html template:
<template name="home">
{{#if isAdmin}}
<h1>student</h1>
{{/if}}
<div id="inner_home">
{{renderPage innerHome}}
</div>
</template>
Is it possible to have a different route /home/tools : 'home_tools render it's template not in the highest main content div but within my child div inner_home?
EDIT:
I am evaluating an approach that just uses JQuery and the callback function of the meteor-router method to grab the template i am looking for and add it directly to the concerning div tag.
Something like:
var foundTemplate = _.template($('#item-template').html())
$('#div_example').html(foundTemplate); //
If anyone has a better approach, please let me know.
Sure.
client.js
Meteor.Router.add({
'/home/tools': function() {
Session.set("homeTemplate", "tools");
return 'home';
},
'/home/admin': function() {
Session.set("homeTemplate", "admin");
return 'home';
}
});
home.js
Template.home.homeTemplateTools = function() {
return Session.equal("homeTemplate", "tools");
};
Template.home.homeTemplateAdmin = function() {
return Session.equal("homeTemplate", "admin");
};
home.html
<template name="home">
{{#if isAdmin}}
<h1>student</h1>
{{/if}}
<div id="inner_home">
{{#if homeTemplateTools}}
{{> homeTools}}
{{/if}}
{{#if homeTemplateAdmin}}
{{> homeAdmin}}
{{/if}}
</div>
</template>
Related
I have a basical layout with a sidebar. I want to hide the sidebar when I am on /users/profile. How can I do it using the if helper? Is there a way to get the current file name?
Here is the code:
<div class="wrapper">
{{#if user}}
<nav id="sidebar">
<div class="sidebar-header">
<h3>Options</h3>
</div>
<ul class="list-unstyled components">
<li class="active">
<h1>
Home
</h1>
</li>
<li>
<h1>
Chat
</h1>
</li>
<li>
<h1>
About us
</h1>
</li>
</ul>
</nav>
{{else}}
{{/if}}
With the current code if a user is logged in the sidebar is showed , otherwise it is hidden. I want the same behaviour also if I am on users/profile.
Your routing is handled by Node.js. Somewhere in your Node.js you probably have instructions on what to do when the user opens /users/chatlist or /users/search, e.g. you select which template to render. Once you know the request is for route /users/profile, you use a JSON payload for your handlebars to properly render the page. You can customize that payload as you wish, including adding some helper fields. For example it may look like:
{
user: { ... },
sidebar: {
visible: false
}
}
Then inside your template, you may use it for a conditional check, like:
{{#if sidebar.visible}}
<nav id="sidebar">
...
</nav>
{{/if}}
I'm trying to render a user's name on a navbar, but that navbar is in a partial folder and none of the routes in node.js renders navbar.ejs because it is just a partial. I'm using ejs-mate library for having a reusable layout component to be used for other's html components so that I don't need to copy and paste codes (Following the DRY principle), why? because ejs doesn't have a built in reusable layout component's feature, and other libraries are not maintained by other developers.
Currently
I have two routes
app.get('/', passport.isAuthenticated, function(req, res) {
res.render('home' {
user: req.user
});
});
app.get('/anotherRoute', function(req, res) {
res.render('test');
});
I have two ejs files
home.ejs
<%- layout('boilerplate') %>
<h1>Home Page</h1>
another.ejs
<%- layout('boilerplate') %>
<h1>Another Page</h1>
Those two ejs files are using boilerplate.ejs for the layout, so I don't to copy and paste the same codes over and over again.
boilerplate.ejs - In this file I have include navbar and some other partials.
<!DOCTYPE html>
<html lang="en">
<head>
<% include ./partials/navbar %>
</head>
<body>
<header>
<% include ./partials/header %>
</header>
<div class="container">
<main>
<%- body -%>
</main>
<footer>
<% include ./partials/footer %>
</footer>
</div>
</body>
</html>
This is the navbar file, and take a look at the navbar-right class,
if user is logged in render name
else render about and signup
navbar.ejs
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">
StartupClone
</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li>About</li>
<% if (!user) { %>
<li>Signup</li>
<li>Login</li>
</ul>
<% } else{ %>
<li><a><%= user.profile.name %></a></li>
<% } %>
</div>
</nav>
The problem right now it is only working with a route that i pass a user's object to but not other routes. I know that I have to pass the user's object to every single route but it is not good because again I have to copy and paste the codes again in every single route which is against the DRY principle,
Imagine if have 40 routes that need to use that navbar, it would be a nightmare for me :(
If you guys have any idea on how to hack this, feel free to lend your thoughts on how to tackle this problem.
It is compulsory to pass the object you are trying to access on the view.
One thing you could do is store user object in session and in view check session for user object.
That way you will have to pass just the session object atleast I use this trick for this problem.
In your middleware passport.isAuthenticated on succesful login save the user object in session
module.exports.isAuthenticated = function(req, res, next) {
/*
check for valid credentials
*/
if (success)
{
req.session.user = foundUserInfo;
}
next();
}
and in your partial view
<!-- navbar.ejs -->
<nav class="navbar navbar-default" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">
StartupClone
</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li>About</li>
<% if (!session.user) { %>
<li>Signup</li>
<li>Login</li>
</ul>
<% } else{ %>
<li><a><%= session.user.profile.name %></a></li>
<% } %>
</div>
Now you just have to pass session object for each route.
app.get('/', passport.isAuthenticated, function(req, res) {
res.render('home' {
user: req.user,
session: req.session
});
});
app.get('/anotherRoute', function(req, res) {
res.render('test', {session: null});
});
You have to pass an object that has the used properties for each res.render() call, there's no way around it. It's even in the usage example in ejs-mate's documentation.
Having said that, there is a way to keep your code DRY. Use a function that sets shared properties on an input object, then for each route set the unique properties it requires:
function setSharedProperties(req, data) {
if(!(data instanceof Object)) {
data = {};
}
data.user = req.user;
return data;
}
app.get('/', passport.isAuthenticated, function(req, res) {
res.render('home', setSharedProperties(req))
});
app.get('/anotherRoute', function(req, res) {
res.render('test', setSharedProperties(req, {prop1: val1}));
});
I have a named view called myView in which I have two div elements that I want to show conditionally using ng-show based on whether the value of $scope.states['currentState'] is 'A' or 'B'. The value of $scope.states['currentState'] is changed when an anchor tag is clicked which calls the doStuff function on the myController controller.
The issue I am having is when the anchor tag is clicked and the doStuff function is clicked, it shows on the console that the value of $scope.states['currentState'] has been modified, but its not updating the myView named view accordingly.
Following is the code for app.js, myView.html and index.html files. The index.html file is being used as a <div ui-view></div> in an index.ejs file that I am rendering using Express with Node.js.
app.js
var app = angular.module("app", ['ui.router']).
config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
views: {
'': { templateUrl: 'partials/index.html' },
'myView#home': {
templateUrl: 'partials/myView.html',
controller: 'myController'
},
'myOtherView#home': {
templateUrl: 'partials/myOtherView.html',
controller: 'myController'
}
}
});
}])
app.controller("myController", ['$scope', function($scope){
var states = ['A', 'B'];
$scope.states = states;
$scope.states['currentState'] = states['currentState'] || 'A';
$scope.doStuff = function(toState) {
//doing stuff
$scope.states['currentState'] = toState;
console.log($scope.states['currentState']);
};
} ]);
index.html
<div class="main">
<div class="container">
<div class="row margin-bottom-40">
<div class="col-md-12 col-sm-12">
<div class="content-page">
<div class="row">
<div ui-view="myView"></div>
<div ui-view="myOtherView"></div>
</div>
</div>
</div>
</div>
</div>
</div>
myView.html
<div ng-controller='myController'>
<div ng-show="states['currentState'] == 'A'">
//displaying this div if the currentState is A
<a ng-click="doStuff('B')">Do stuff and show B</a>
</div>
<div ng-show="states['currentState'] == 'B'">
//displaying this div if the currentState is B
</div>
</div>
Could somebody help me understand that why am not getting the div with states['currentState'] == 'B' shown, even when I see the value of console.log($scope.states['currentState']) changed from 'A' to 'B' when the doStuff function is called in myController?
Edit:
Here is the demo of the issue I am facing.
Okay So I was mistaken in my comment.
The real issue was that you used {{}} in your ng-show which is not needed as these expect to take angular expressions.Also I would make current state a property of your scope as at the moment you are trying to make it a property of an array inside your scope.
Hope that helps! Below is the modified code for your view:
<div ng-controller='MainCtrl'>
<div ng-show="currentState === 'A'">
<a ng-click="doStuff('B')">Do stuff and show B</a>
</div>
<div ng-show="currentState === 'B'">
<a ng-click="doStuff('A')">Do stuff and show A</a>
</div>
</div>
EDIT: Working plunker http://plnkr.co/edit/1llQMQEdxIwu65MNoorx?p=preview
Polymer provides access to elements by id via this.$['foo']. However, I find that I am unable to access elements by id that are in nested templates.
<template>
<div id="foo"></div>
<template>
<div id="bar"></div>
</template>
</template>
In this situation this.$.foo works but this.$.bar does not. Are you able to access elements inside of a nested template by id and if so how?
In my code I'm using a conditional template to include some html if a attribute is true. I was providing this functionality in javascript by editing the html but think that conditional templates more clearly show what is going on and I would prefer to use this method.
Using this.$.<id> syntax does work with inner templates. The problem in your example is that the inner <template> is inert, and it isn't rendered. I've rewritten your example so that the inner <template> is rendered, and you can see that the this.$.<id> syntax works for both the outer and inner <template>s:
<polymer-element name="my-element">
<template>
<div id="foo">
<p>Old foo</p>
<template if="{{showBar}}">
<div id="bar">
<p>Old bar</p>
</div>
</template>
</div>
<button on-tap="{{changeContent}}">Change Content</button>
</template>
<script>
Polymer({
showBar: true,
changeContent: function() {
this.$.foo.querySelector('p').textContent = 'New foo';
this.$.bar.querySelector('p').textContent = 'New bar';
}
});
</script>
</polymer-element>
Pressing the "Change Content" button finds the #foo and #bar <div>s and updates the text of the contained <p>s.
You can try out the answer at http://jsbin.com/roceta/edit.
I'm trying to preserve the full markup of a template, including the class on the root node when using a marionette region. I'm also trying to avoid creating an extra wrapping div. I've solved the problem, but in a way which I don't think is satisfactory.
I am creating and rendering a layout like this:
MyApp = new Backbone.Marionette.Application();
MyApp.addRegions({
mainRegion: "#main"
});
AppLayout = Backbone.Marionette.Layout.extend({
template: '
<div class="row">
<div class="col-md-8"></div>
<div class="col-md-4"></div>
</div>
'
});
var layout = new AppLayout();
MyApp.mainRegion.show(layout);
layout.show(new MenuView());
And the result is that my template is rendered like this:
<div id="main">
<div>
<div class="col-md-8"></div>
<div class="col-md-4"></div>
</div>
</div>
Notice, the class="row" is missing from the root node of the template. It appears that marionette is removing the root div from my template, and then wrapping the contents in a new div.
I have managed to hack a solution to this like this
AppLayout = Backbone.Marionette.Layout.extend({
template: '
<div><!-- sacrificial div -->
<div class="row">
<div class="col-md-8"></div>
<div class="col-md-4"></div>
</div>
</div>
',
onRender: function () {
// get rid of that pesky wrapping-div
// assumes 1 child element.
this.$el = this.$el.children();
this.setElement(this.$el);
}
});
I'm adding an extra root div (my sacrificial div) to my template which marionette removes, and then I'm telling marionette to use the first child as the layouts 'el' (as per Turning off div wrap for Backbone.Marionette.ItemView).
This seems crazy!
Can somebody suggest a better way?
EDIT: n.b. I'd like to keep all the template logic in the template, so don't want to have to use code in my view to specify the class on the root node - if I do this, I end up with a maintenance headache.
Try with
AppLayout = Backbone.Marionette.Layout.extend({
template: '
<div class="col-md-8"></div>
<div class="col-md-4"></div>
',
className: "row"
});
AppLayout is missing its region(s).
AppLayout = Backbone.Marionette.Layout.extend({
template: '
<div class="row">
<div id="region1" class="col-md-8"></div>
<div id="region2" class="col-md-4"></div>
</div>
',
regions: {
region1: '#region1',
region2: '#region2'
}
});
Then at your instantiated layout:
layout.region1.show(new MenuView());
layout.region2.show(new MenuView());
Instead of assigning the template to a string, compile the HTML into a template function with underscore. Something like this:
template: _.template(
'<div class="row">' +
'<div class="col-md-8"></div>' +
'<div class="col-md-4"></div>' +
'</div>'
)