Sending a variable from express middleware to jade - javascript

I have a keystonejs app setup, using express and jade.
I have a fullscreen background image setup in my default.jade file which defines various imports and also the header and footer of the site.
I am trying to rotate the image based on a selection of images that are located in the mongodb via mongoose.
I was having issues getting it to work, so i have just been trying to get the variable to print correctly in the header of my page.
I have the following code setup in my root middleware file, as part of the export.initLocals file:
exports.initLocals = function(req, res, next) {
var locals = res.locals;
locals.navLinks = [
{ label: 'Home', key: 'home', href: '/' },
{ label: 'News', key: 'blog', href: '/news' },
{ label: 'Creations', key: 'creation', href: '/creations' },
{ label: 'Contact', key: 'contact', href: '/contact' }
];
locals.user = req.user;
locals.imageURL = 'variable set';
WelcomeImage = keystone.list('WelcomeImage');
WelcomeImage.model.findOneRandom(function(err, result) {
if (!err) {
locals.imageURL = result.Image.url;
console.log(result.Image.url); // 1 element
} else {
console.log(err); // 1 element
}
});
next();
};
and within my default.jade file i have:
//- HTML BODY
body
#background-image
//- HEADER
#header: .container
//- Customise your site's navigation by changing the navLinks Array in ./routes/middleware.js
//- ... or completely change this header to suit your design.
div(role="navigation").navbar.navbar-default
.container-fluid
.navbar-header
.visible-xs.navbar-brand-xs.a(href='/')
img(align:"left",src="/images/wild_logo.png",border=0,width=45,height=45)
button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse')
span.sr-only Toggle navigation
span.icon-bar
span.icon-bar
span.icon-bar
.collapse.navbar-collapse
.hidden-xs.navbar-brand.a(href='/')
img(align:"left",src="/images/wild_logo.png",border=0,width=95,height=95)
ul.nav.navbar-nav.navbar-left
each link in navLinks
li(class=(section == link.key ? 'active' : null)): a(href=link.href)= link.label
ul.nav.navbar-nav.navbar-right
if user
if user.canAccessKeystone
li: a(href='/keystone') Open Keystone
li: a(href='/keystone/signout') Sign Out
else
if imageURL
| #{imageURL}
else
There is no variable
//li: a(href='/keystone/signin') Sign In
//- BODY
#body
Now every time i load the page i see the variable rendered in the page, but it is set as 'variable set' and never get's set to the actual URL.
Now if i watch the console, the correct value is sent to the console on the line directly after it's set on locals.
So, any ideas what i'm doing wrong. I'm very new to express/jade so it's likely i'm overlooking something in the order things are done?
Thanks in advance!
Cheers
Gareth

Try moving next() inside the callback function of the findOneRandom call. You're basically telling node it's safe to move on before the callback is complete.
Glad that helped. Thanks!

Related

How to create a single Gatsby Page to show and filter all blog posts by tag/category

Hello i'm building a blog using Gatsby and Netlify CMS. I started off from the gatsby-starter-netlify-cms template.
I have the /blog page where i currently display all posts and also a list of all the tags.
When user clicks on a tag, it is currently redirected to the tags/name-of-tag page, where a list of all posts tagged like that is displayed.
What i want instead is to directly filter the list on the /blog page.
This way i'll have only one page to display and filter blog posts by tag (and possibly by term search).
So the tag links should redirect to /blog?tag-name or something like that.
I'm not sure how to tell Gatsby to create a single page with possibility to inject a filter value in order to pass it to the page query..
This is how /tags/ pages are created currently:
// Tag pages:
let tags = []
// Iterate through each post, putting all found tags into `tags`
posts.forEach((edge) => {
if (_.get(edge, `node.frontmatter.tags`)) {
tags = tags.concat(edge.node.frontmatter.tags)
}
})
// Eliminate duplicate tags
tags = _.uniq(tags)
// Make tag pages
tags.forEach((tag) => {
const tagPath = `/tags/${_.kebabCase(tag)}/`
createPage({
path: tagPath,
component: path.resolve(`src/templates/tags.js`),
context: {
tag,
},
})
})
This is my blog page query (the one i want to be able to filter by tag):
export const blogPageQuery = graphql`
query BlogPageTemplate {
markdownRemark(frontmatter: { templateKey: { eq: "blog-page" } }) {
frontmatter {
image {
childImageSharp {
fluid(maxWidth: 2048, quality: 80) {
...GatsbyImageSharpFluid
}
}
}
heading
subheading
}
}
allMarkdownRemark(
sort: { order: DESC, fields: [frontmatter___date] }
filter: { frontmatter: { templateKey: { eq: "blog-post" } } }
) {
edges {
node {
id
fields {
slug
}
excerpt(pruneLength: 180)
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
featuredpost
featuredimage {
childImageSharp {
fluid(quality: 50, maxWidth: 512) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
}
`
I'm not sure how to tell Gatsby to create a single page with
possibility to inject a filter value in order to pass it to the page
query.
You can't. The only way to filter data in a page query is by passing data using the context. Like you are doing in with the tags page:
createPage({
path: tagPath,
component: path.resolve(`src/templates/tags.js`),
context: {
tag,
},
})
The easiest and native approach is to use the /tag/tag-name page, which is intended to filter by tag name, otherwise, you will need to get the URL parameter and using it in a JavaScript filter function to filter all the data from the page query. Since you are missing the rendering part of your blog page... Something like this approach should work:
const BlogTemplate=({ data }){
if(typeof window !== "undefined"){
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const urlTag= urlParams.get('tag');
const filteredPosts= data.allMarkdownRemark.edges.node.filter(node=> node.frontmatter.tag.includes(urlTag))
const loopData= urlParams.has('tag') ? filteredPosts : data
}
return loopData.allMarkdownRemark.edges.node.map(node=> <h1 key={node.title}>{node.title}</h1>)
}
Note: of course, adapt it to your needs but get the idea.
Note also that you will need to wrap all your window logic in the typeof window !== "undefined" condition, since in the SSR window (and other global objects) are not available.
The key part is to use wether use data or your filteredPosts depending on the existance of the URL parameter, so if it exists, you will need to filter the data otherwise, you need to use the "default" data (unfiltered).
It's difficult to guess how the code will behave but the idea relies on changing your iterable data depending on the URL, use some hooks if needed.
According to your query, it seems that your blogs don't contain any tag field in the blog template query so you will need to add it to allow the filter loop to work.
Once the code is working, you will need to add the proper controls to avoid the code-breaking when some field is missing, but the idea and the path to follow is this.

How to navigate to a page in Framework7

I am just starting with Framework7 and I want to navigate to another page when a click a paragraph. I tried a few things and googled for the past hour but no luck.
Here is the code I have in my-app.js:
var $$ = Dom7;
var app = new Framework7({
// App root element
root: '#app',
// App Name
name: 'My App',
// App id
id: 'com.myapp.test',
// Enable swipe panel
panel: {
swipe: 'left',
},
view : {
stackPages: true
},
// Add default routes
routes: [
{
name: 'about',
path: '/about/',
url: 'about.html',
},
{
name: 'index',
path: '/index/',
url: 'index.html'
},
{
name: 'preview',
path: '/preview/',
url: './preview.html'
},
],
// ... other parameters
});
var mainView = app.views.create('.view-main');
$$('p').on('click', function(){
mainView.router.navigate('/preview/');
});
When I click a paragraph nothing happens and there are no errors when I run the inspector.
What am I doing wrong?
Thank you.
You can do that by using this:
self.app.router.navigate('/preview/', {reloadCurrent: true});
Also make sure that html layout like this:
<div class="view">
<!-- Initial Page, "data-name" contains page name -->
<div data-name="preview" class="page">
<!-- Scrollable page content -->
<div class="page-content">
preview page content
</div>
</div>
</div>
Update 1: In F7 ver 4 you can use this:
app.views.main.router.navigate('/login/', {reloadCurrent: true});
app.mainView.router.navigate('/preview/')
Try this if the answer #Anis offered but its supposed to work that answer
It should work using : app.router.navigate('/preview/')
To go some specific routes you can use
app.views.main.router.navigate('your route');
If you have a scenario like after create some task and you have to redirect to listing page, where if you use
if(formSubmitSuccessfull){
app.views.main.router.navigate('your router')
}
It will redirect to the previous page (In this case form create) after redirect to the listing page, When you try to back to the dashboard or other page from listing
In this case you have to use
app.views.main.router.back({
url: '/your router where you want to go.../',
force: true,
ignoreCache: true
})

Setting layout and title in sails

I want to fetch a view and its specific layout in controller and once that is done,i want to use variables inside the controller in the view and layout.
I want to use title in the layout and other variables in the view.
Currently i have this and i cant change the title in my layout
hi: function (req, res) {
res.view('noder/home',{ layout: 'layout' },{
locals: {
title : 'hello'
}
})},
How can i change the title in the layout on controller basis and use other variables within my view?
Try this:
hi: function(req, res) {
res.view('noder/home', {
layout: 'layout',
title: 'hello'
});
}
Make sure your layout.ejs file has following line in title tag
<title><%- title %></title>

Backbone.js router/views logic

I am writing my first Backbone.js application and I am having some trouble figuring out the best way to program it. I have 2 main views:
Shows an index of all my models.
Shows a specific model for editing.
But #2 has many different 'modules' like I can edit the 'news' section, or 'about' section etc...
All these modules are in a navigation bar.
That navigation bar is hidden when I am displaying view # 1 (index of all models). It is visible in view # 2(a specific model) in order to navigate between different modules.
I have routes setup like this:
routes: {
'', 'index',
'communities': 'index',
'communities/:id': 'main',
'communities/:id/news', 'news',
'communities/:id/about', 'about'
},
So my question is, when 'news' or 'about' action is called, do I add a navigation bar in each method? Isn't that redundant? I am going to have like 8-10 different modules, add navigation bar each time seems very repetitive. Is there a better way?
The only time I want the navigation bar to be hidden is when showing index.
I came across this same problem when I created my first somewhat complex Backbone app. Along with your concern of redundant code, I was concerned about events bound to my navbar that may not get unbound as the navigation bar changed. To solve the problem, I wound up creating a view hierarchy, with one manager view managing the navigation bar a whole, and separate views for each type of navigation menu I wanted to display, which would be passed to the manager view to render to the page.
Here's an example of my implementation.
Before we start, here is a close function I added to Backbone's View prototype which unbinds events and removes the view
Backbone.View.prototype.close = function() {
if(this.beforeClose) { this.beforeClose(); }
this.remove();
this.unbind();
}
First, here is my Manager View. Its render function closes whatever menu is currently displayed and replaces it with the one passed to it as view. While slightly redundant, I created an explicit empty function to make my router code easier to understand.
var App.Views.SubNavBar = Backbone.View.extend({
currentView: null,
el: '#subnav-wrap',
render: function(view) {
if(this.currentView) { this.currentView.close(); }
this.currentView = view;
this.$el.html(view.el);
},
empty: function() {
if(this.currentView) { this.currentView.close(); }
this.currentView = null;
}
});
Second, here is a base view that all of my specific navigation menu views extend. Since they will all have the same tagName, className, id, and initialize and render functions, this keeps repetition to a minimum
var App.Views.SubNavBase = Backbone.View.extend({
tagName: 'ul',
className: 'nav nav-pills',
id: 'subnav',
template: _.template($('#tmpl-subnav').html(),
initialize: function() {
if(this.setLinks) { this.setLinks(); }
this.render();
},
render: function() {
this.$el.html(this.template({links:this.links}));
return this;
}
});
Here is an example of a view for a specific navigation menu. You can see that all I need to do is define the links I want to appear in the menu. When I instantiate this view, the functions of SubNavBase will handle populating the view with the required HTML. Note that I also have some events attached to this view.
var App.Views.Projects.DisplayNav = App.Views.SubNavBase.extend({
setLinks: function() {
this.links = {
'Edit Project': {
icon: 'edit',
class: 'menu-edit',
href: '#projects/'+this.model.get('id')+'/edit'
},
'Add Group': {
icon: 'plus',
class: 'menu-add-group',
href: '#projects/'+this.model.get('id')+'/groups/new'
},
'Delete Project': {
icon: 'trash',
class: 'menu-delete',
href: '#'
}
}
},
events: {
'click a.menu-delete' : 'delete'
},
delete: function(e) {
e.preventDefault();
// here goes my code to delete a project model
}
});
Now, here is the underscore.js template I use to turn the links object above into a list of <li> elements. Note that I use <# instead of <% for my templates since this is a rails app and rails already uses <%
<script type="text/template" id="tmpl-subnav">
<# _.each(links,function(link, title) { #>
<li>
<a href="<#= link.href #>" class="<#= link.class #>">
<i class="icon-<#= link.icon #>"></i>
<#= link.title #>
</a>
</li>
<# }); #>
</script>
Finally, to put it all together, here is an example Router function that creates and renders the nav menu. The steps that occur are as follows:
App.Views.Projects.DisplayNav gets passed a model and populates its this.el with the corresponding HTML, as determined by the underscore.js template
App.SubNavBar has its render function called with the new menu view
App.SubNavBar checks to see if there is currently another menu in the navigation bar; if so, it calls its view's close() function
App.SubNavBar finally appends the passed view's HTML to itself, maintaining a reference to the view for later use
I've included only the relevant parts of the router code
var App.Routers.Projects = Backbone.Router.extend({
routes: {
'projects/:id' : 'display'
},
display: function(id) {
var p = projects.get(id);
var subnav = new App.Views.Projects.DisplayNav({model:p})
App.SubNavManager.render(subnav); // App.SubNavManager is an instance of App.Views.SubNavBar
}
});
The benefit to all of this is that I can now attach events to my menu-specific views, and the manager view will take care of unbinding them if the user navigates to different content and the menu changes.
Of course, there are many other patterns you can use to handle navigation menus, but hopefully this will help you on the path.
Try this:
routes: {
'', 'index',
'communities': 'index',
'communities/:id': 'main',
'communities/:id/:section': 'openSection'
},
openSection : function(id, section){
if( section ){
this.addNavigationBar();
}
switch( section ){
case 'news' :
this.news();
break;
case 'about' :
this.about();
break;
default:
this.main();
}
}
If your url contents a section you will add the navigation bar and then call you normal method as you have.

ExtJS 4 - How to download a file using Ajax?

I have a form with various textfields and two buttons - Export to Excel and Export to CSV.
The user can provide the values to different fields in the form and click one of the buttons.
Expected behaviour is that an Ajax request should be fired carrying the values of fields as parameters and the chosen file (Excel/CSV as per the button click) should get downloaded (I am not submitting the form as there needs to be done some processing at the values before submit).
I have been using the following code in success function of Ajax request for the download:
result = Ext.decode(response.responseText);
try {
Ext.destroy(Ext.get('testIframe'));
}
catch(e) {}
Ext.core.DomHelper.append(document.body, {
tag: 'iframe',
id:'testIframe',
css: 'display:none;visibility:hidden;height:0px;',
src: result.filename,
frameBorder: 0,
width: 0,
height: 0
});
The above code has been working fine in the case when the file is created physically at the server. But in my current project, the file is not created at the server, rather the contents are just streamed to the browser with proper headers.
Thus, is there a way to download a file using Ajax when the file is not present at the server physically? Just to add that I have a long list of parameters which I need to send to the server and hence can not add them all to the src of iframe.
Could anyone guide at this?
Thanks for any help in advance.
You may use component like this:
Ext.define('CMS.view.FileDownload', {
extend: 'Ext.Component',
alias: 'widget.FileDownloader',
autoEl: {
tag: 'iframe',
cls: 'x-hidden',
src: Ext.SSL_SECURE_URL
},
stateful: false,
load: function(config){
var e = this.getEl();
e.dom.src = config.url +
(config.params ? '?' + Ext.urlEncode(config.params) : '');
e.dom.onload = function() {
if(e.dom.contentDocument.body.childNodes[0].wholeText == '404') {
Ext.Msg.show({
title: 'Attachment missing',
msg: 'The document you are after can not be found on the server.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
})
}
}
}
});
Put it somewhere in viewport, for example:
{
region: 'south',
html: 'South',
items: [
{
xtype: 'FileDownloader',
id: 'FileDownloader'
}
]
}
Do not forget to require it in your viewport class:
requires: [
'CMS.view.FileDownload'
]
Action handler may look like this:
var downloader = Ext.getCmp('FileDownloader')
downloader.load({
url: '/attachments/' + record.get('id') + '/' + record.get('file')
});
It's very important to have Content-Disposition header in response, otherwise nothing is downloaded.
Regards go to http://www.quispiam.com/blog/post.php?s=2011-06-09-download-a-file-via-an-event-for-extjs4
This thing works for me.

Categories

Resources