I need to have different layouts when render a view (Expressjs 2.x). How to change them ?
For example:
res.render('follow_page', {layout:'layout_2'}, {name:'test'})
res.render('user_page', {layout:'layout_1'}, {user:'test1'})
For Express 2.x and earlier, you'll want to merge the 2 objects, giving it a locals property:
res.render('follow_page', { layout: 'layout_2', locals: { name: 'test' } });
res.render('user_page', { layout: 'layout_1', locals: { name: 'test1' } });
Related
I'm creating a blog where each blog page will show a map using leaflet with a GPX route on it. Below the map will be some statistics and some text and images.
I have the text and images part defined in mark down so i figured the way to handle this would be to define my gpx filename in the frontmatter like so:
---
title: Awesome Blog Post Title
author: Cancrizan
date: 2021-01-04
gpxFile: activity4835702422
---
BLOG POST here
where the field gpxFile refers to a a file in my project src/gpx/activity4835702422.gpx.
I've written a transformer plugin that will read in the GPX file so that it can be queried like this:
query MyQuery {
allActivity4835702422Gpx {
edges {
node {
geometry {
coordinates
}
}
}
}
}
and outputs something like this:
{
"data": {
"allActivity4835702422Gpx": {
"edges": [
{
"node": {
"geometry": {
"coordinates": [
[
-1.2134187016636133,
52.92038678191602,
29.399999618530273
],
[
-1.2134256586432457,
52.92039977386594,
29.399999618530273
],
...,
]
}
}
}
]
}
},
"extensions": {}
}
I want to access that node based on the frontmatter of the markdown file and i'm not sure how?
Can anyone suggest a solution or am i going about this the wrong way?
The structure and the mindset you've followed is perfectly valid, the only part you're missing is to pass the gpxFile to your template in order to create another query based on that parameter.
Your gatsby-node.js should look like something like this:
const path = require("path")
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(
`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
frontmatter {
title
author
date
gpxFile
slug
}
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
const postTemplate= path.resolve(`src/templates/post.js`);
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
const path = node.frontmatter.path
createPage({
path: `/posts/${node.frontmatter.slug}`,
component: postTemplate,
context: {
gpxFile: node.frontmatter.gpxFile,
path: node.frontmatter.slug
}
})
})
}
The idea is to use context API to pass data (gpxFileData and path) to your template (postTemplate) to use it as a filter for your markdown files.
In your postTemplate, your query should look like:
export const postData = graphql`
query getArticleData($path: String!, $gpxFile: String) {
post: markdownRemark (fields: {frontmatter: { slug: { eq: $path }}}) {
html
excerpt (pruneLength: 350)
frontmatter {
title
author
date
gpxFile
slug
}
}
gpxFileData: allFile(relativePath: { in: $gpxFile })
# your gpx data here
}
}
`;
It's quite self-explanatory, basically, you are passing via context the necessary data to make a query in your template (gpxFileData) from your gatsby-node.js. There, you can create a new query, allFile, filtering by relativePath (you may need to access to file directly or use absolutePath, test it in localhost:8000/___graphql) and retrieve the whole data using props.data.post and post.data.gpxFileData.
Disclaimer: I'm assuming that you've set your filesystem (gatsby-source-filesystem) properly to use allFile across your .gpx files.
I was looking at creating a simple generic reusable text-only widget in apostrophe-cms so I made the module text-only-widgets.
with the following code
In lib/modules/text-only-widgets/index.js
module.exports = {
extend: 'apostrophe-widgets',
label: 'Section Heading',
beforeConstruct: function (self, options) {
console.log('options.addFields : ', options.addFields)
options.addFields = [
{
name: 'sectionHeading',
label: 'Section Heading',
type: 'string'
}
].concat(options.addFields || [])
}
};
In lib/modules/text-only-widgets/views/widget.html
{{ data.widget.sectionHeading }}
Now I tried using the above widget in one of my pages as below:
<div>
{{
apos.area(data.page, 'aboutUsDescriptionTwo', {
widgets: {
'text-only': {
addFields: [
{
name: 'sectionDescription', // set these fields dynamically
label: 'Section Description', // set these fields dynamically
type: 'string'
}
]
}
}
})
}}
</div>
As shown in the below image, notice even after passing the addFields with label as Section Description to the text-only widget doesn't override the defaults set in the index.js.
I tried console.log on the options.addFields in index.js but it logs undefined as show below, I also tried few different variations but none works.
The addFields option is at the module level, it controls what fields exist for all instances of this widget. It is not an option that can be passed to individual instances of the widget via apos.area. Thus you can't do it in this way.
The correct approach is to create a second module which uses extend to extend the first one and addFields, at the module level, to add the additional field. Then, in your area, you can offer just one of the two widget types, or both, as is appropriate to your needs.
Thus in addition to lib/modules/text-only-widgets/index.js you would also have:
// in lib/modules/text-plus-description-widgets/index.js
module.exports = {
extend: 'text-only-widgets',
label: 'Section Heading and Description',
addFields: [
{
name: 'sectionDescription',
label: 'Section Description',
type: 'string'
}
]
};
And this widget would also have its own widget.html.
We're using Keystone (version 4.0.0-beta.8) as our Node.js CMS for the first time for a project and we're really liking it. However we're running into a vital missing feature of the CMS and that is adding custom components to the admin views.
What we would like to achieve is to add a custom React component to one of the Keystone admin screens and populate it with data from a field watch function
This component should only be visible for one of the list models. Right now, the only way to do this, is to edit one of the core files:
/node_modules/keystone/admin/client/App/screens/Item/index.js
and add some conditional rendering around it:
{(this.props.params.listId === 'my-list-model') ? (
<MyComponent>{this.props.params.listId}</MyComponent>
) : null }
This works, but is not ideal off-course since you're overwriting core files. But I could overcome this short-come if I knew how I can feed this custom component with data from the Keystone list model declaration:
In: /server/models/MyListModel.js
import keystone from 'keystone';
import { someFunction } from './myFunctions';
var MyListModel = new keystone.List('MyListModel', {
map: { name: 'title' },
});
MyListModel.add({
title: { type: String, required: true },
data: { type: Types.Code, language: 'json', watch: 'title', value: watchTitle, noedit: true },
});
MyListModel.register();
function watchTitle(callback) {
if (this.title) {
function cb(error, result) {
if (result) {
// Send result to React Component in Admin screen
}
callback(error, result);
}
someFunction(this.title, cb);
} else {
callback(null, '');
}
}
Does anyone bump into the same issue or have any clue how to send data to a custom component in the react admin view of Keystone?
Thanks a lot!
Faced with a problem at work with ExtJS
There is such a code - a new class (view)
Ext.define('FormApp.view.ElementContainer', {
extend: 'Ext.Container',
alias: 'widget.elemcontainer',
initComponent: function() {
this.items = [{
layout: {
type: 'hbox',
align: 'middle'
},
items: [
{ xtype: 'component',
html: ' Поиск  '},
{ xtype: 'textfield',
width: 495,
name: 'search'
},
{ xtype:'component',
html:'  '},
{ xtype: 'button',
text: 'Найти',
width: 80,
action: 'searchTreeData'}
]}
];
this.callParent();
}
});
Then in the controller I write code like this to get the value of the textfield
Ext.define('FormApp.controller.ControlOne', {
extend: 'Ext.app.Controller',
views: ['ElementContainer', 'TreeView'],
init: function() {
this.control({
'elemcontainer button[action=searchTreeData]': {
click: this.searchTree
},
'elemcontainer textfield[name=search]':{change: this.str}
});
},
searchTree: function(searchStr) {
var dat = Ext.widget('elemcontainer');
var str = dat.down('textfield').getValue();
alert (str.getValue());
},
str: function()
{alert('OK');}
});
I can not get the value of a text field in extJS
How to access the elements to get their values?
Thanks in advance and sorry for my clumsy English
The problem is that by using Ext.widget(...) in searchTree(), you're creating a new instance of the class (be sure to check the docs), rather than getting the of the component that already exists.
Another issue is that str is already the "value" of the textfield, so calling getValue() on str.getValue() won't get you very far either.
So a few suggestions:
Update you searchTree method to pass the correct arguments. Since this method is getting called on the click event of a button, the arguments will be those of the click event for Ext.button.Button : searchTree( btn, e, opts ) {...}
Once you have the correct arguments being passed to searchTree(), you can then use the component selector methods to get the existing instance of the container. For example, since the button is already a descendant of the container, you can do the following to get the correct instance of the component:
var ctr = btn.up( "elemcontainer" )
And now that you have the correct instance of the container, you can again use one of the component selector methods to find the textfield:
var str = ctr.down( 'textfield' ).getValue()
I have several records of the same type that I want to show on the screen. I thought about creating several panels that will print the data of each record. I chose this solution because data structure is too complex to be printed in a simple grid. Here is a simplified example of that structure :
{
label: 'myLabel',
{
attr1: 'value1',
attr2: 'value2'
}
startValidityDate: oneDay,
endValidityDate: anotherDay
}
I try to add dynamically nested panels in my current panel :
var myStore = new Ext.data.Store({
id: 'myStore',
restful: true,
idProperty: 'OID',
root: 'tbas',
proxy: myProxy,
reader: myReader,
autoLoad: false,
listeners: {
'load': function(data){
var records = data.getRange();
var currStore = null;
for(var i=0; i<records.length; i++) {
currStore = new Ext.Panel({
layout:'vBox',
items: [{
xtype: 'textfield',
fieldLabel: I18nManager.get('label_ttbaUI_label'),
name: 'tbaLabel',
value: records[i].data.tbaLabel
},{
xtype: 'textfield',
fieldLabel: I18nManager.get('label_ttbaUI_label'),
name: 'tbaOid',
value: records[i].data.tbaoid
}]
});
recordList.add(currStore);
console.log("------------------------------");
console.log(currStore);
}
recordList.doLayout();
}
}
});
var recordList = new Ext.Panel({
id: 'recordList',
renderTo: 'recordPart',
layout:'vBox',
title: I18nManager.get('label_ttbaUI_selected_tariffs')
});
recordList.doLayout();
In the firebug console, the UI objects seems to be ok.
My problem is that the recordList elements are not visible
I can see that they exist in the FB console, but they are not well printed on the screen.
Did I forgot something that make the elements hidden ? or bad printed ?
I'm sure that it is a CSS problem, some trouble with ext-all.css : when I remove the content of that CSS, I can see my fields
There must be something wrong in the way I wrote the code so that it causes the render problem WDYT ???