New to Sails.js how can I route to a view without including the layout.ejs? - node.js

New to Sails.js I've read over the docs but I don't see a way to NOT include the layout.ejs located in /views/.
I'm trying to implement Angular into my app and when I go
$routeProvider.when('/',{
controller: 'HomeController',
templateUrl: '/home'
})
So then in my routes I expect to be able to do:
'GET /home': { view: 'angular/home.ejs' }
But what happens is it loads home.ejs but also injects layout.ejs so it becomes an endless loop of injecting layout.ejs loading angular scripts from layout, then trying to load home.ejs which loads layout.ejs so on so forth
So how can I do this?
I know I can put views in /asset/angular/home.html but I would like to have .ejs so that I can render a different view (e.g. user is not logged in) or something.
Any information on how I can render views without injecting layout.ejs would be great, thanks!
Update:
So by doing this:
'GET /home': {
view: 'home',
locals: {
layout: false
}
}
it makes it work, I don't know if there is a global way so I don't have to flag layout: false for each one

To disable layouts globally, you can put layout : false under /config/view.js.
To disable layout individually, leave /config/view.js alone and do something like this in your routes:
'GET /home': {
view: 'home',
locals: {
layout: false
}
}

Related

Changing script files used on the front end of a node backed website

I'm making a solitaire game using NodeJs and Express backend and phaser.io frontend. It will offer different layouts of cards to play.
I have the game working "just fine" alpha state, but with a single layout of cards setup in HTML like:
<script src="js/gameboards/data.js"></script>
<script src="js/tableau.js"></script>
where data.js is the file that describes the card layout, and tableau.js is the game logic. I have several different files in /gameboards and for the testing/building, I just change the filename when I want to change the layout.
data.js is not a JSON file, it's a JS object
let gameboard = {
info: {
title: "Standard",
description: "6 columns of 5 cards"
},
[...]
deal: function() {
for (let i = 0; i < this.vars.allstacks; i++) {
etc...
That contains simple object data as well as object methods that define patterns and repetition (like multiple stacks, pyramids, etc) so it can't really be made into a JSON or other straight data file.
What I want to do is present a list of anchors/links to the player of the layout options. They'll click the link to get sent into /game with the layout they chose.
The method I thought of was to have /index send POST-data containing the layoutname. Then, in /game
router.post('/', function(req, res, next) { ... })
with the HTML template and res.render containing
<script src="js/gameboards/<layoutname>.js"></script>
to call the right file.
Is there a better way of doing this than what I listed above? It seems kludgey to me as if there would be a more 'nodey' and elegant way to do it.
To do it in the way you describe you need to enable a templating engine like handlebars or Pug. For pug I do something like this:
In server:
const cdnAction = process.env.S3_CDN_ACTIVE;
app.route('*')
.get( (req, res) => {
const cdnCSSPath = `https://s3.amazonaws.com/${process.env.S3_BUCKET_CDN}/audience.css.gz`;
res.render('home', { cdnActive, cdnPath, cdnCSSPath });
});
In pug file 'home.pug' rendered above:
head
if cdnActive === 'true'
link(href=cdnCSSPath rel='stylesheet' type='text/css')
else
link(href='/style/embed.css' rel='stylesheet' type='text/css')

How to know if block content is empty in Jade

How can we check if block is exist in Jade, I'm trying to make a template in Jade where in i have following block in layout.jade and in login.jade i have extended layout.jade. Now when in account.jade i will have both left and main content blocks
extend layout.jade
div(class='col-md-4')
block leftcontainer
div(class='col-md-8')
block content
Now in login.jade i want to hide
div(class='col-md-4')
block leftcontainer
and make the main content full page, if leftcontainer is not there. It should look like this
div(class='col-md-12')
block content
if user is on some page i want to hide left column block. Say if user is in login page then
If I got it right, you wan't to hide a block if a user (e.g. a user who is logged in to your app) is at a certain page. If this is right do something like this:
Example with Express & Passport:
// server
app.get('/login', function(req, res){
if(req.isAuthenticated()) {
res.render('login', { 'user' : req.user } );
} else {
res.render('login', { 'user' : null } );
}
});
// layout
extend layout.jade
if user !== null
div(class='col-md-12')
block content
else
div(class='col-md-4')
block leftcontainer
div(class='col-md-8')
block content
In general you can work with other conditional statements in Jade, without Passport, this was just an example. You may want to take a look at the reference, especially under conditional or case.

Marionette, how to change view's template on fly

I'm doing a single page webApp with Marionette and RequireJs, basically it is a Layout view nested with a lots of ItemView and compositeView. I want the page change to a new design by one click, so I want to ask a best practice about changing the view's template.
say, I got my view like this, and its templates are included by requirejs's text plugin:
define([
'text!templates/designNo1/template.html'
], function(template) {
var MyView = Backbone.Marionette.ItemView.extend({
/*Template*/
template: template,
initialize: function() {
......
},
onRender: function() {
......
}
});
return SkillView;
});
every view and theirs subviews are defined like this. and their templates located in "template/designNo1" folder. now I got a set of new design located in "template/designNo2", and I want apply it to the page, but I found there is no chance to do this, because the views are already loaded by RequireJs and the template's path are hard-coded. of course, I can override the view's template when create the view's instance, but if I do so, I must load all the new design in the upper-module which create the instance, that looks bad, and the new design are keep coming, it gonna be a mess soon.
so, any advice?
From what I can tell, it sounds like you are wanting to change the template on a view depending on different circumstances. Marionette's ItemView has a getTemplate() function that is called when rendering it. You can specify a function that determines which template to render from there.
Another approach might be to simply change the template of the view and re-render it. You could do that on a click event easily:
HTML
<div class="content"></div>
<button class="btn">Change Template</button>
Javascript
var template1 = '<div>Template 1</div>';
var template2 = '<div>Template 2</div>';
var ItemView = Backbone.Marionette.ItemView.extend({
template: template1
});
var itemView = new ItemView({ el: '.content' });
itemView.render();
$('.btn').click(function(e) {
e.preventDefault();
itemView.template = template2;
itemView.render();
});

Node.js, Express, Jade - Separate layout files

I'm working on some project with Node.js, Express and Jade, where I'd like to seperate layout files. Inside the main file is already separated header, but I don't know how to do this for sublayout where I need to pass data. In this case I need to pass data to widgets for every view on page, but in the route would be too many things to load data into widgets instead of some easy solution which I'm looking for.
I could do this thing on the way which I described above - to load data in view with every request, but this is somehow time & cpu consuming.
Another way I'm thinking of is to create some sublayout for widgets in which I'd load data once and then would be available all the time without calling data from DB in all requests. What's the best way to do that?
I work with mustache but I think you can use a similar strategy that I do.In most of the mustache templates that I use there is a common header and footer section.Along with the scripts and css files.I have created a separate partials file that exports these partials
.For instance my partial file looks like this.
exports.partials = function (isAuthenticated)
{
var menu;
isAuthenticated ?
menu = {
header: '',
footer: ' '
} :
menu = {
header: '',
footer: ''
}
return menu;
};
exports.staticResources = {
bootstrap :'//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.2/css/bootstrap-combined.min.css',
fonts : '//netdna.bootstrapcdn.com/font-awesome/3.0/css/font-awesome.css',
jquery : '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js'
};
I have another method called generatePartials which as the name suggest generate the partials for my templates
exports.generatePartials = function(isAuthenticated){
var menu = resources.partials(isAuthenticated);
var partials = {
header : menu.header,
footer : menu.footer,
bootstrap : resources.staticResources.bootstrap,
fonts :resources.staticResources.fonts,
jquery :resources.staticResources.jquery,
};
return partials;
};
Now while rendering the template all I have to do is this
app.get('/routeName',function (req, res){
var partials = require('../helpers').generatePartials(req.isAuthenticated());
return res.render('viewName.html', partials);
};
And that's it.

How to update an AngularJS directive when using $route and partials

http://jsfiddle.net/MTzJF/36/
The JSFiddle above is set up to explain the issue. But, basically:
HTML:
<breadcrumb></breadcrumb>
<div ng-view></div>
Angular Directive & Routing:
angular
.module('app', [])
.directive('breadcrumb', function() {
return {
restrict: 'E',
template: "<ul class='breadcrumb'><li ng-repeat='node in path'><a ng-href='{{node.url}}'>{{node.label}}</a></li></ul>",
replace: true,
controller: Ctrl1
}
})
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
template: '<h1>{{pgTitle}}</h1>',
controller: Ctrl2
});
}]);
Controllers
function Ctrl1($scope) {
$scope.path = [{
label: 'Home',
url: '#/'}];
$scope.pgTitle = "Home"
}
function Ctrl2($scope, $routeParams) {
$scope.path = [{
label: 'Home',
url: '#/'},{
label: 'Node 2',
url: '#/node2'}];
$scope.pgTitle = "Node 2"
}
​
I expect that changing $scope.path in Ctrl2 will update the breadcrumb directive, but it's not happening. I have to believe it has something to do with their relative scopes, but simply don't understand it well enough to see what. I've read dozens of articles and StackOverflow posts on it, but nothing is specific enough to let me see what I'm missing.
I'm hoping someone can point me in the right direction.
Thanks much!
nz
The reason your fiddle is not working is because ( like you rightly identified ) of scope issue. Your Ctrl1 is the Controller that controls the scope for your directive. The directive is looking for a variable called path which is an array of path's. If we have a look at the path variable in that scope it seems to be containing just 1 value inside of it.
function Ctrl1($scope) {
$scope.path = [{
label: 'Home',
url: '#/'}];
$scope.pgTitle = "Home"
}
Now you wish to change this variable path in another controller Ctrl2. I am assuming that you are attempting to have the scope of Ctrl2 "inherit" from the scope of Ctrl1. To achieve this, first check on which element Ctrl2 is defined. Is that element ( html element ) a child of the element of Ctrl1 ?
From your HTML :
Element of Ctrl1 : <breadcrumb></breadcrumb>
Element of Ctrl2 : <div ng-view></div>
For Ctrl2 to be child of Ctrl1 : your HTML structure should look like :
<breadcrumb>
<div ng-view></div>
</breadcrumb>
If we make this change to your code, it doesnt work yet. This is because when angular looks at the directive for <breadcrumb> it has no idea what it should do with the stuff that is inside of that node. <breadcrumb> is a html node. Since it is a node, it can contain content / other nodes inside of it. When you replace this breadcrumb node with the template, you should also give angular instructions to the effect : "If you find stuff inside of me, put it here". This is how you do it.
Modify your directive code to be :
.directive('breadcrumb', function() {
return {
restrict: 'E',
template: "<div><ul class='breadcrumb'><li ng-repeat='node in path'><a ng-href='{{node.url}}'>{{node.label}}</a></li></ul><div ng-transclude></div></div>",
replace: true,
transclude : true,
controller: Ctrl1
}
})
There are few differences / changes here.
Added an attribute called transclude to the directive object and set it to true.
Wrap the whole template so that it can be returned as a single HTML element.
Specify the place where you want the contents of the to go. Notice the ng-transclude. That is the place where the contents go.
You will notice now that the content is getting replaced now. Except the path is not getting updated with the path value from the child controller. Have a look at this https://stackoverflow.com/a/14049482/1057639 answer. It is an amazing explanation of scope / prototypical inheritance. Basically, your write to path is creating a new path variable in the child scope that is overshadowing the parent path ( which is being used by the directive).
Your best bet in this scenario is to create an addPath function in the parent scope and use it ( in child scopes ) to add new Path when you define new subviews.
Here is a fiddle that does all these.

Resources