Gatsby-plugin-sitemap, custom config, need to integrate pages and markdown using custom resolvePages and Serialize, what does this line of code do? - reactjs

just starting with javascript and react thanks to Gatsby so excuse me if this is a total newbie question. Also just starting with posting on stackoverflow, usually just consuming content, sorry about that and if my post is incomplete or unclear in anyway.
I am building a website using GatsbyJs, and want to setup a proper sitemap using gatsby-plugin-sitemap, however i am strugling to understand what the following line of code does so i can try and customize de code to do what I need, which is integrate the pages and blog posts on the sitemap, and adding a proper lastmod when applicable. I am breaking my head but cannot get the last part to work, that is, adding lastmod when it is a blog post.
on gatsby-config.js:
{
resolve: `gatsby-plugin-sitemap`,
options: {
// Exclude specific pages or groups of pages using glob parameters
// See: https://github.com/isaacs/minimatch
// The example below will exclude the single `path/to/page` and all routes beginning with `category`
output: '/',
excludes: [`/legal/*`,`/gracias`],
query: `
{
site {
siteMetadata {
siteUrl
}
buildTime(formatString: "YYYY-MM-DD")
}
allSitePage {
nodes {
path
}
}
allMarkdownRemark(filter: {frontmatter: {path: {regex: "/blog/"}}}) {
nodes {
frontmatter {
path
date
}
}
}
}
`,
resolvePages: ({
allSitePage: { nodes: allPages },
allMarkdownRemark: { nodes: allPosts },
}) => {
const pathToDateMap = {};
allPosts.map(post => {
pathToDateMap [post.frontmatter.path] = { date: post.frontmatter.date };
});
const pages = allPages.map(page => {
return { ...page, ...pathToDateMap [page.path] };//what does this line of code do???
});
return pages;
},
serialize: ({ path, date, buildTime }) => {
let entry = {
url: path,
changefreq: 'monthly',
priority: 0.5,
};
if (date) {
entry.priority = 0.7;
entry.lastmod = date;
} else {
entry.lastmod = buildtime;
}
return entry;
}
}
}
For your knowledge both develop and build are succesful, the sitemap is generated as sitemap-index and sitemap-0.xml, and the output is there but no page has lastmod on it.
Thank you all for your help,

With this:
return { ...page, ...pathToDateMap [page.path] };
You are merging objects using the spread operator (...). So, you are returning an object that is the result of all properties of the page and pathToDateMap(at page.path position).
For example:
let person = {
firstName: 'John',
lastName: 'Doe',
age: 25,
};
let job = {
jobTitle: 'Web developer',
location: 'USA'
};
let employee = {
...person,
...job
};
console.log(employee)
In the snippet above, employee is the result of merging person and job.
The lastmod parameter is not added by default unless your source has it (WordPress normally has) but you can follow this answer https://stackoverflow.com/a/70297982/5585371 to create your own.

Related

Block Context in editor.BlockEdit filter

I’m trying to extend a core gutenberg block so that it has a couple of additional options only when it’s contained in a certain custom made block. I thought block context would make this a simple task — set the container block to providesContext and use the blocks.registerBlockType filter to add usesContext to the core block. In the editor.BlockEdit filter, I thought I’d be able to get context from the block’s props. However, context is not one of the props. To make sure I wasn’t doing anything wrong around context, I used the editor.BlockEdit filter on one of my custom blocks that I knew was receiving block context correctly. It doesn’t have context as one of its props either.
Does anyone know of a way to get block context into the editor.BlockEdit filter?
For reference, here is a snippet of the code so far:
title: 'Card',
icon: 'images-alt2',
category: 'utdesign_system',
description: '',
attributes: {
blockName: {
type: 'string',
default: 'card',
}
},
providesContext: {
'card/blockName': 'blockName',
},
edit,
save,
} );
function addImageContext( settings ) {
if( settings.name == 'core/image' ){
settings.usesContext = Object.assign( ['card/blockName'] );
}
return settings;
}
addFilter(
'blocks.registerBlockType',
'gutenstrap/image-context',
addImageContext
);
const addImageControls = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
const {
name,
attributes,
setAttributes,
context,
isSelected,
} = props;
//do stuff with context
};
}, "addImageControls" );
addFilter(
'editor.BlockEdit',
'gutenstrap/image-context',
addImageControls
);```
It is now documented https://developer.wordpress.org/block-editor/reference-guides/block-api/block-context/
registerBlockType( 'my-plugin/record-title', {
title: 'Record Title',
category: 'widgets',
usesContext: [ 'my-plugin/recordId' ],
edit( { context } ) {
return 'The record ID: ' + context[ 'my-plugin/recordId' ];
},
save() {
return null;
},
} );

Why am I getting these errors in my AngularJS (TypeScript) component?

I have AngularJS component in TypeScript news.component.ts that calls a google.service.ts to get news RSS via some service that converts XML to JSON (not important).
So I have this method in NewsComponent:
getNewsHeadlines(): any {
this.googleService.getNewsHeadlines().then(headlines => {
this.newsHeadlines = headlines;
this.$scope.$apply();
});
return;
}
I have this model defined for each news item:
export interface NewsHeadline {
title: string;
content: string;
description: string;
link: string;
}
I have this method in GoogleService service:
getNewsHeadlines() {
const feed = 'http://rss.newsru.com/top/big/';
const url = 'https://api.rss2json.com/v1/api.json?rss_url=' + encodeURIComponent(feed);
return this.$http.get(url).then((response: any) => {
let items = response.data.items;
let results = items.map(result => {
let newsHeadline: NewsHeadline = {
title: 'string',
content: 'string',
description: 'string',
link: 'string'
};
return newsHeadline;
});
return this.$q.all(results);
});
};
Why do I keep getting these error messages in Chrome console?
Is my getNewsHeadlines method in GoogleService written correctly?
What does it mean, when I click on Error link, there is tons of humanly unreadable garbage, that doesn't explain anything.
Your method doesn't need $scope.$$apply. It is being handled by the httpservice, as is angular context.
This is how it should look like:
getNewsHeadlines(): any {
this.googleService.getNewsHeadlines().then(headlines => {
this.newsHeadlines = headlines;
});
return;
}

No match on any of the rangeBehaviors specified in RANGE_ADD config

I have the following mutation:
export default class AddTaskMutation extends Relay.Mutation {
static fragments = {
classroom: () => Relay.QL`
fragment on Classroom {
id,
}`,
};
getMutation() {
return Relay.QL`mutation { addTask }`;
}
getFatQuery() {
return Relay.QL`
fragment on AddTaskPayload {
classroom {
taskList,
},
taskEdge,
}
`;
}
getConfigs() {
let rangeBehaviors = {};
let member_id = 'hasNewMessages(true) member_id(' + Global.fromGlobalId(this.props.createdByMember)['id'] + ')';
rangeBehaviors[''] = 'append';
rangeBehaviors['hasToDo(true)'] = 'append';
rangeBehaviors['hasToDo(false)'] = 'append';
rangeBehaviors['isStart(true)'] = 'ignore';
rangeBehaviors['isStart(false)'] = 'append';
rangeBehaviors[`${member_id}`] = 'append';
rangeBehaviors['hasNewMessages(true) member_id(null)'] = 'ignore';
return [
{
type: 'RANGE_ADD',
parentName: 'classroom',
parentID: this.props.classroomId,
connectionName: 'taskList',
edgeName: 'taskEdge',
rangeBehaviors,
}
];
}
getVariables() {
return {
title: this.props.title,
instruction: this.props.instruction,
start_date: this.props.start_date,
end_date: this.props.end_date,
is_archived: this.props.is_archived,
is_published: this.props.is_published,
is_preview: this.props.is_preview,
productId: this.props.productId,
classroomId: this.props.classroomId,
createdByMember: this.props.createdByMember,
subTasks: this.props.subTasks,
students: this.props.students,
};
}
}
When running my application I get the following 2 warnings:
warning.js:44 Warning: RelayMutation: The connection taskList{hasNewMessages:true,member_id:null} on the mutation field classroom that corresponds to the ID Classroom:35 did not match any of the rangeBehaviors specified in your RANGE_ADD config. This means that the entire connection will be refetched. Configure a range behavior for this mutation in order to fetch only the new edge and to enable optimistic mutations or use refetch to squelch this warning.
and
warning.js:44 Warning: Using null as a rangeBehavior value is deprecated. Use ignore to avoid refetching a range.
Since the other rangeBehaviors work, I assume there must be a syntactical error when declaring 2 variables in one behavior - in this case hasNewMessages and memeber_id.
I've looked for an answer for this, but I just cannot find any. The docs don't seem to cover this edge case either.
EDIT: I also tried rangeBehaviors['hasNewMessages(true),member_id(null)'] = 'ignore'; (comma as a separator) but with no success.
After inspecting the source code of Relay (file RelayMutationQuery.js) I could see what array key it was searching for in the array of all rangeBehaviors. I could then update my code to the correct formatting.
Since I haven't found anything on the web about this edge case, I'll post my solution here - perhaps someone will find it helpful in the future.
When having 2 (or more) variables for a rangeBehavior, you need to separate them with a period (.). Also, when passing null you don't pass it explicitly - just omit it from its' variable.
For example:
Right:
rangeBehaviors['hasNewMessages(true).member_id()'] = 'ignore';
Wrong:
rangeBehaviors['hasNewMessages(true),member_id(null)'] = 'ignore';

Joining AngularFire paths on a value/key is not working (merging user profiles into records)

I am developing an application with Firebase (1.0) and Angular (1.4). The problem I'm having is to ensure the data in view are synchronised with Firebase, while fetching denormalised data coming from two tables in Firebase:
The book table looks like this:
books:
-JyDpkQrUozE3L7flpo: {
title: "title1",
author: {-JyDpkQrUozE3L7fl1x: true}
},
-JyDpkQrUozE3L7flp1: {
title: "title2",
author: {-JyDptrrrezE3L7flKx: true}
},
-JyDpkQrUozE3L7flp2: {
title: "title3",
author: {-JyDpkQrUozE3L7f222: true}
}
The author table looks like this:
authors:
-JyDpkQrUozE3L7flKx: {
firstname: "jacques",
lastname: "smith"
},
-JyDptrrrezE3L7flKx: {
firstname: "bill",
lastname: "halley"
},
-JyDpkQrUozE3L7f222: {
firstname: "john",
lastname: "ford"
}
My controller looks like this:
.controller('booksController', ['$scope', '$firebaseArray', function($scope, $firebaseArray) {
var ref = new Firebase("https://[PATH].firebaseio.com/";
$scope.bookList = $firebaseArray(ref.child('books'));
$scope.authorList = $firebaseArray(ref.child('authors'));
$scope.getAuthor = function(id) {
return $scope.authorList.$getRecord(id);
};
});
And my html looks like this:
<pre>
<p ng-repeat="book in books" ng-init="author = getAuthor(book.author)">
The author of {{book.title}} is {{author.firstName}} {{author.lastName}}.
</p>
</pre>
The desired output of the above should be: "The author of title1 is Jacques Smith. The author of title2 is Bill Halley ...". What I'm getting however is: "The author of title 1 is. The author of title2 is..." So the author in my html returns a blank.
Any idea?
What I see is that your json data for books has author as an object. This is what is passed into the $getRecord method.The ID of the author is a key, not a value.
I believe if you structure your data like this:
books: {
-JyDpkQrUozE3L7flpo: {
title: "title1",
author: "-JyDpkQrUozE3L7fl1x"
}
-JyDpkQrUozE3L7flp1: {
title: "title2",
author: "-JyDptrrrezE3L7flKx"
},
-JyDpkQrUozE3L7flp2: {
title: "title3",
author: "-JyDpkQrUozE3L7f222"
}
It should work, but it has been a long time since I have used firebase.
Brandon's answer is technically-correct answer to the posed question. I'm going to elaborate a bit on what would be a better way to join these records.
I've actually answered this exact question in a fiddle, and also provided a more sophisticated, elegant, and simpler solution of how to cache and merge user profiles into objects. I'll reiterate the details of how this works here.
app.factory('NormalizedPosts', function($firebaseArray, userCache) {
var PostsWithUsers = $firebaseArray.$extend({
// override $$added to include users
$$added: function(snap) {
// call the super method
var record = $firebaseArray.prototype.$$added.call(this, snap);
userCache.$load( record.user ).$loaded(function( userData ) {
record.userData = userData;
});
// return the modified record
return record;
}
});
return PostsWithUsers;
});
Here I've decided to use a cached list of users, since they are likely to be highly redundant, and this provides an elegant way to keep everything in sync. It's not strictly necessary--we could look them up right there in $$added, but that leaves some edge cases to be handled. So a cache of synchronized users feels right here.
So here's the caching utility:
app.factory('userCache', function ($firebase) {
return function (ref) {
var cachedUsers = {};
// loads one user into the local cache, you do not need to
// wait for this to show it in your view, Angular and Firebase
// will work out the details in the background
cachedUsers.$load = function (id) {
if( !cachedUsers.hasOwnProperty(id) ) {
cachedUsers[id] = $firebaseObject(ref.child(id));
}
return cachedUsers[id];
};
// frees memory and stops listening on user objects
// use this when you switch views in your SPA and no longer
// need this list
cachedUsers.$dispose = function () {
angular.forEach(cachedUsers, function (user) {
user.$destroy();
});
};
// removes one user, note that the user does not have
// to be cached locally for this to work
cachedUsers.$remove = function(id) {
delete cachedUsers[id];
ref.child(id).remove();
};
return cachedUsers;
}
});
And here's a gist putting it all together.
Note that, if we know that when our controller is destroyed, that the data will no longer be useful, we can clean up the listeners and memory by calling $destroy. This isn't strictly necessary and could be a premature optimization, but is probably worth mentioning for users with complex production apps that have tens of thousands of records to track:
app.controller('...', function(NormalizedPosts, userCache, $scope) {
$scope.posts = new NormalizedPosts(<firebase ref>);
$scope.$on('$destroy', function() {
$scope.posts.$destroy();
userCache.$dispose();
});
});
I realize this is an old question but I thought I share my solution for those who are still googling.
Like Brandon mentions you shouldn't have the auther as an object, just and ID (reference)
Your data should look like this:
{
books: {
JyDpkQrUozE3L7flpo: {
title: "title1",
authorId: JyDpkQrUozE3L7fl1x
},
JyDpkQrUozE3L7flp1: {
title: "title2",
authorId: JyDptrrrezE3L7flKx
},
JyDpkQrUozE3L7flp2: {
title: "title3",
authorId: JyDpkQrUozE3L7f222
}
},
authors: {
JyDpkQrUozE3L7flKx: {
firstname: "jacques",
lastname: "smith"
},
JyDptrrrezE3L7flKx: {
firstname: "bill",
lastname: "halley"
},
JyDpkQrUozE3L7f222: {
firstname: "john",
lastname: "ford"
}
}
}
Note that I changed the author to authorId to be more explicit what is a authoer object and what is just the id. Now lets get all the books and the author.
app.factory('BooksWithAuthor', function($firebaseArray, $firebaseObject) {
const books = firebase.database().ref('books')
const authors = firebase.database().ref('authors');
const bookList = $firebaseArray.$extend({
$$added: function(snap) {
const book = $firebaseArray.prototype.$$added.call(this, snap);
book.author = $firebaseObject(authors.child(book.authorId));
return record;
}
});
return new bookList(books)
});
app.controller('BookCtrl, function(BooksWithAuthor) {
$scope.BookList = BooksWithAuthor;
});
And then in your HTML just do
<div ng-repeat="book in booklist">
{{ book.title }} was written by {{ book.author.firstname }} {{ book.author.lastname }}
</div>

How to write structured data with JSON writer?

How can I include hasOne associated model data in the JSON POST?
Structured data is required by my web API in the form of:
{
id: 1234,
name: 'Aaron Smith',
address: {
address1: '1925 Isaac Newton Sq',
address2: 'Suite 300',
city: 'Reston',
state: 'VA',
zip: 20190
}
}
#nonino
I think I know how to do it but I am also having a similar problem. I can't actually get my associations to give me the associated data. Anyway from what I have scrounged on the internet make a custom writer like this or just in the default writers getRecordData: function(record,operation)
Here is my custom writer
Ext.define('Wakanda.writer', {
extend: 'Ext.data.writer.Json',
// alternateClassName: 'SimplyFundraising.data.WakandaWriter',
alias: 'writer.wakanda',
writeAllFields: false,
getRecordData: function(record,operation) {
debugger;
Ext.apply(record.data,record.getAssociatedData());
debugger;
var isPhantom = record.phantom === true,
writeAll = this.writeAllFields || isPhantom,
nameProperty = this.nameProperty,
fields = record.fields,
data = {},
changes,
name,
field,
key;
if (writeAll) {
// console.log("getRecordData1", this, arguments);
fields.each(function(field){
if (field.persist) {
debugger;
name = field[nameProperty] || field.name;
data[name] = record.get(field.name);
} else {
}
});
} else {
changes = record.getChanges();
debugger;
// console.log("getRecordData2", this, arguments, changes);
for (key in changes) {
if (changes.hasOwnProperty(key)) {
field = fields.get(key);
name = field[nameProperty] || field.name;
data[name] = changes[key];
}
}
if (!isPhantom) {
debugger;
data[record.idProperty] = record.getId();
if(operation.action !== 'destroy'){
data[record.stampProperty] = record.get(record.stampProperty);
}
}
}
return {'__ENTITIES': [data]};
}
});
The key I think is in the getRecordData where I have a statement Ext.apply(record.data,record.getAssociatedData()); If record.getAssociatedData does indeed return your data then the Ext.apply statement will merge your current record.data with your record.getAssociatedData into 1 json file. At least this is what I hope happens. Can't test until I get my associations setup correctly.
Hope this helps,
Dan
getRecordData: function(record,operation) {
debugger;
Ext.apply(record.data,record.getAssociatedData());
debugger;

Resources