Next.js: getStaticPaths for nested dynamic routes - javascript

Imagine you have this data structure:
const data = {
posts: [{
id: 1,
title: "Post 1"
slug: "post-1"
}, {
id: 2,
title: "Post 2"
slug: "post-2"
}],
comments: [{
id: 1,
postId: "post-1",
text: "Comment 1 for Post 1"
}, {
id: 2,
postId: "post-1",
text: "Comment 2 for Post 1"
}, {
id: 3,
postId: "post-2",
text: "Comment 1 for Post 2"
}]
}
An you have the following route /posts/[postId[/[commentId]
so the Next.js structure folder is: posts/[postId]/[commented].js
Then you need to generate the static paths for this routes.
I'm coded the following:
export async function getStaticPaths() {
const { posts, comments } = data
const paths = posts.map((post) => {
return comments
.filter((comment) => comment.postId === post.slug)
.map((comment) => {
return {
params: {
postId: post.slug,
commentId: comment.id
}
}
})
})
}
But it's not working. The throwed error was:
Error: Additional keys were returned from `getStaticPaths` in page "/clases/[courseId]/[lessonId]". URL Parameters intended for this dynamic route must be nested under the `params` key, i.e.:
return { params: { postId: ..., commentId: ... } }
Keys that need to be moved: 0, 1.
How I can "map" or "loop" the data to a proper returned format?
Thanks in advance!

The problem seems to be that your returning this from getStaticPaths data with a wrong shape:
[
[ { params: {} }, { params: {} } ],
[ { params: {} } ]
]
The correct shape is:
[
{ params: {} },
{ params: {} },
{ params: {} }
]
Just tried this and it works.
export async function getStaticPaths() {
const paths = data.comments.map((comment) => {
return {
params: {
postId: comment.postId,
commentId: comment.id
}
}
});
console.log(paths);
return {
paths,
fallback: false
}
};
It generates 3 urls:
/posts/post-1/1
/posts/post-1/2
/posts/post-2/3
Is that what you need?

Like mention #Aaron the problem is for double array of filter y el map.
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: ...
}
Doc 📚 ➡ https://nextjs.org/docs/basic-features/data-fetching#the-paths-key-required

Related

Vue how push object with specific key to array

I'm working on an vue-application where I have a component for driving licenses.
I have the following:
data() {
return {
custom_licenses: [],
basic_licenses: []
}
}
within my methods, I have this:
regular_licenses() {
this.$store.dispatch("license/read").then(response => {
response.licenses.map((license, key) => {
// PUSH LICENSES WITH TYPE 'BASIC' TO this.basic_licenses
// PUSH LICENSES WITH TYPE 'CUSTOM' TO this.custom_licenses
});
});
},
and in my created() i have this:
created() {
this.regular_licenses()
}
The response from my dispatch, returns this:
licenses:
[
{
id: 1,
type: 'basic',
name: 'AMa'
},
{
id: 2,
type: 'basic',
name: 'A2'
},
{
id: 3,
type: 'basic',
name: 'C'
},
{
id: 4,
type: 'custom',
name: 'C1'
},
{
id: 5,
type: 'custom',
name: 'D'
},
and so on...
]
Now I want to loop through the array and separate or push them into custom_licenses and basic_licenses based on the type-attribute - how can I achieve that?
Try this
regular_licenses() {
this.$store.dispatch("license/read").then(response => {
response.licenses.map((license, key) => {
switch (license.type)
case 'basic':
this.basic_licenses.push({ ...license });
break;
case 'custom':
this.custom_licenses.push({ ...license });
break;
});
});
},
Update your Code Block:
response.licenses.map((license, key) => {
// PUSH LICENSES WITH TYPE 'BASIC' TO this.basic_licenses
if(license['type'] == 'basic') {
//deep clone
let tmpLicense = JSON.parse(JSON.stringify(license));
basic_licenses.push(tmpLicense);
} else if(license['type'] == 'custom') {
// PUSH LICENSES WITH TYPE 'CUSTOM' TO this.custom_licenses
//deep clone
let tmpLicense = JSON.parse(JSON.stringify(license));
custom_licenses.push(tmpLicense);
}
});

JS filter some part from an array of objects

I've this array.
const routes = [
{
path:'/dashboard',
text: "Dashboard"
},
{
path:'/disputes',
text: "Disputes"
},
{
children: [
{
text: "Create Suburb",
path: "/create-suburb"
},
{
text: "View and Update Suburb",
path: "/view-suburb"
}
]
},
{
children: [
{
text: "Create user",
path: "/create-user"
},
{
text: "View and Update users",
path: "/view-users"
}
]
}
]
and I've this array
const permissions = ['/dashboard','/view-suburb'];
What I want is filter out objects from the array where there is not in the permissions array.
My expected out put is this
const routes = [
{
path:'/dashboard',
text: "Dashboard"
},
{
children: [
{
text: "View and Update Suburb",
path: "/view-suburb"
}
]
},
]
Note that two objects are completely removed and some part of the third object also removed. How do I achieve this using JS?
What I've done upto now is this
items.filter(e=>{
if(e.path){
return permissions.includes(e.path)
}else{
}
})
Hope my question is clear to you.
You could do it with a reduce - filter alone won't work here as you're actually transforming child arrays rather than purely filtering the top level array items
routes.reduce((result, route) => {
const { path, children } = route;
if (children) {
const filteredChildren = children.filter(child => permissions.includes(child.path));
// case where some child routes match permissions
if (filteredChildren.length !== 0) {
return [ ...result, { ...route, children: filteredChildren }];
}
}
// case where path is present and permissions includes path
if (path && permissions.includes(path)) {
return [ ...result, route ];
}
// case where there's no match between path and permissions
return result;
}, []);
Try this!!
const routes = [
{
path:'/dashboard',
text: "Dashboard"
},
{
path:'/disputes',
text: "Disputes"
},
{
children: [
{
text: "Create Suburb",
path: "/create-suburb"
},
{
text: "View and Update Suburb",
path: "/view-suburb"
}
]
},
{
children: [
{
text: "Create user",
path: "/create-user"
},
{
text: "View and Update users",
path: "/view-users"
}
]
}
]
const permissions = ['/dashboard','/view-suburb'];
let result = [];
permissions.map(permission=>{
routes.map(route=>{
if(route.hasOwnProperty('children')){
route.children.map((r,i)=>{
if(r.path == permission){
route.children = route.children.splice(i);
route.children = route.children.slice(-1);
result.push(route)
}
});
}else{
if(route.path == permission){
result.push(route)
}
}
});
})
console.log(result)
This one also worked for me.
var newData = [];
$.each(routes, function (key, value) {
debugger
var data = this;
if (data.path) {
if (permissions.includes(data.path)) {
newData.push(data);
}
}
else {
var data2 = data;
$.each(data2, function (key, value1) {
$.each(value1, function (key, value) {
var data = value;
if (data.path) {
if (permissions.includes(data.path)) {
newData.push(data);
}
}
});
});
}
})
Ideally, you should check the access to the route inside the canActivate guard and navigate the user further to the appropriate route.

How to normalize data by different schemas based on the entity parent's value with normalizr?

I'd like to normalize data in this format:
[
{
id: 3,
status: "active",
created: "2017-07-03T15:36:11+02:00",
published: "2017-07-03T15:38:07+02:00",
linkables: [
{
data: {
content: "Text content of the linkable",
id: 200
}
linkable_type: "content",
module_id: 3,
module_type: "case_study"
},
{
data: {
content: "https://foobar.wee/url_of_the_media.png",
id: 205
}
linkable_type: "media",
module_id: 3,
module_type: "case_study"
},
]
},
...
]
into something like this:
{
entities: {
contents: {
200: {
content: "Text content of the linkable",
id: 200
}
},
media: {
205: {
content: "https://foobar.wee/url_of_the_media.png",
id: 205
}
},
linkables: {
3_case_study_content_200: {
data: 200, // in the contents key
linkable_type: "content",
module_id: 3,
module_type: "case_study"
},
3_case_study_media_205: {
data: 205, // in the media key
linkable_type: "media",
module_id: 3,
module_type: "case_study"
}
},
caseStudies: {
3: {
id: 3,
status: "active",
created: "2017-07-03T15:36:11+02:00",
linkables: ["3_case_study_content_200", "3_case_study_media_205"]
}
}
},
result: [3]
}
Using the 2.x version of the normalizr makes this easy thanks to this PR https://github.com/paularmstrong/normalizr/pull/132 that adds a function one can use to dynamically decide what scheme to use when normalizing by the value in the parent object.
However, in the version 3.x.x this additional function did not make it. According to the docs, I'd say that schemaAttribute does the job.
Having this piece of code unfortunately does not give me the wanted result...
export const caseStudies = new schema.Entity('caseStudies');
export const media = new schema.Entity('media',);
export const content = new schema.Entity('contents');
export const linkablesSchema = new schema.Entity(
'linkables',
{},
{
idAttribute: (itm) => {
const id = itm.data.id;
return `${itm.module_id}_${itm.module_type}_${itm.linkable_type}_${id}`;
}
}
);
export const linkableSchemaMap = {
content,
media
};
caseStudies.define({
linkables: [linkablesSchema]
});
export const lschema = new schema.Array(linkableSchemaMap, (value, parent) => {
return parent.linkable_type; // this would replace the PR mentioned above?
});
linkablesSchema.define({
data: lschema
});
Thank you for your help!
The most straightforward way that came to mind is to use processStrategy inside of the linkablesSchema options to apply linkable_type value as a property:
export const caseStudies = new schema.Entity("caseStudies");
export const mediaSchema = new schema.Entity("media");
export const contentsSchema = new schema.Entity("contents");
export const linkablesSchema = new schema.Entity("linkables", {}, {
idAttribute: itm => `${itm.module_id}_${itm.module_type}_${itm.linkable_type}_${itm.data.id}`,
processStrategy: (value) => ({
...value,
data: value.data.id,
[value.linkable_type]: value.data,
})
});
linkablesSchema.define({
content: contentsSchema,
media: mediaSchema
})
caseStudies.define({
linkables: [linkablesSchema]
});
normalize(data, [caseStudies]);
And here's the sandbox. Hope it helps.

Sequelize create include existing object

I'm having an issue with sequelize on create with associated model with an "unique" value. It works when I create a "book" with a new "category", but if the "category" already exists it throws an error because it tried to duplicate an "unique" value. Is there a way to make sequelize.create try to use findAndCreate on a associated model?
My setup
Book:
const Book = sequelize.define('book', {
title: Sequelize.STRING,
})
Category:
const Category = sequelize.define('category', {
id: Sequelize.BIGINT,
name: {
type: Sequelize.STRING,
unique: true,
},
})
and the relationship:
Book.belongsTo(Category, { foreignKey: 'category_id' })
So, when i create a book i can write:
Book.create(
{
title: 'Book',
category: {
name: 'Foo'
}
}, {
include: [
{
model: Category
}
]
}
)
if after that I create another book:
Book.create(
{
title: 'Another Book',
category: {
name: 'Foo'
}
}, {
include: [
{
model: Category
}
]
}
)
it throws the error saying category.name must be unique
For anyone with the same question, this was my final solution:
On my services.js I created the function create that receives a request(req) and all my models already initialized.
create: (req, models) => {
const { category, ...rest } = req.body
if (category && category.name && !rest.category_id) {
return models.Category
.findOrCreate({
where: {
name: category.name
}
})
.then(([returnedCategory]) => {
return models.Book.create(
{
...rest,
category_id: returnedCategory.id
},
{ ...defaultOptions(models) }
)
})
} else {
return models.Book.create(rest, { ...defaultOptions(models) })
}
},
defaultOptions looks like this:
const defaultOptions = models => {
return {
attributes: {
exclude: ['category_id', 'created_at', 'updated_at'],
},
include: [
{ model: models.Category, attributes: ['id', 'name'] },
],
}
}

Querying nested objects in PouchDB

I googled some examples and tutorials but couldn't find any clear example for my case.
I get a JSON response from my server like this:
var heroes = [
{
id: 5,
name: 'Batman',
realName: 'Bruce Wayne',
equipments: [
{
type: 'boomarang',
name: 'Batarang',
},
{
type: 'cloak',
name: 'Bat Cloak',
},
{
type: 'bolas',
name: 'Bat-Bolas',
}
]
},
{
id: 6,
name: 'Cat Woman',
realName: 'Selina Kyle',
equipments: [
{
type: 'car',
name: 'Cat-illac',
},
{
type: 'bolas',
name: 'Cat-Bolas',
}
]
}
];
I would like to query for example: "get heroes with equipment type of bolas"
and It should return both hero objects in an array.
I know it is not right but what I am trying to do is to form a map function like this:
function myMapFunction(doc) {
if(doc.equipments.length > 0) {
emit(doc.equipment.type);
}
}
db.query(myMapFunction, {
key: 'bolas',
include_docs: true
}).then(function(result) {
console.log(result);
}).catch(function(err) {
// handle errors
});
Is it possible? If not what alternatives do I have?
P.S: I also checked LokiJS and underscoreDB. However PouchDB looks more sophisticated and capable of such query.
Thank you guys in advance
Your map function should be:
function myMapFunction(doc) {
doc.equipments.forEach(function (equipment) {
emit(equipment.type);
});
}
Then to query, you use {key: 'bolas'}:
db.query(myMapFunction, {
key: 'bolas',
include_docs: true
}).then(function (result) {
// got result
});
Then your result will look like:
{
"total_rows": 5,
"offset": 0,
"rows": [
{
"doc": ...,
"key": "bolas",
"id": ...,
"value": null
},
{
"doc": ...,
"key": "bolas",
"id": ...,
"value": null
}
]
}
Also be sure to create an index first! Details are in the PouchDB map/reduce guide :)

Categories