I have a website which contains kind of blog pages where people can post issues and other people can comment on them.
But when i go to an issue route it gets the data from firebase when the route is the same as the saved slug in the database (not a really good way to do it like this). The data is displayed to the browser but there is one major issue if you reload the page netlify says the page could not be found.
I am loading the data from mounted() lifecycle it might be better to do this on asyncdata()
Here is my code:
mounted() {
self = this;
const issues = firebase.database().ref('Issues/')
issues.once('value', function(snapshot){
snapshot.forEach(function(childSnapshot) {
const data = childSnapshot.exportVal()
if(self.$nuxt.$route.path == "/Issues/"+data.Slug || self.$nuxt.$route.path == "/issues/"+data.Slug ) {
self.allIssuetitels.push(data.Titel)
self.allIssueonderwerpen.push(data.Onderwerp)
self.allteksten.push(data.Tekst)
self.user = data.User
self.profielfoto = data.Profielfoto
self.Slug = data.Slug
}
})
})
const reacties = firebase.database().ref('/Reacties')
reacties.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
const data = childSnapshot.exportVal()
if(self.$nuxt.$route.path == '/Issues/'+data.Slug || self.$nuxt.$route.path == '/issues/'+data.Slug) {
self.reaprofielfoto.push(data.Profielfoto)
self.reauser.push(data.User)
self.allreacties.push(data.Reactie)
}
})
})
},
When i test it local it doesn't give me the page not found error. Is there any way of fixing this issue?
Here is where it does not work:
https://angry-lalande-a5e5eb.netlify.app/issues/3
For something like this where you are enabling users to create new pages and to have lots of user-generated content that is frequently update, you'd be well-served to look into server-side rendering.
https://v2.vuejs.org/v2/guide/ssr.html
I'd suggest, if you want to do that, you should migrate over to Nuxt as it makes things like this much easier.
However, there are quite a few caveats with going SSR - notably you'll need to run and manage a server.
If you don't want to look into SSR, you could use dynamic routes as shown here
const router = new VueRouter({
routes: [
// dynamic segments start with a colon
{ path: '/issue/:id', component: Issue }
]
})
You could then have the Issue component display a skeleton loader or another loading indicator while the content is fetching.
https://vuetifyjs.com/en/components/skeleton-loaders/
Related
I have added a custom domain to the API Gateway due to CORS/Cookies/Infosec and other fun reasons.
I then added the following code to hack the correct domain into my Amplify configuration:
import { Amplify, API } from "aws-amplify"
const myfunc () => {
const amplifyEndpoint = API._restApi._options.endpoints[0]
Amplify.configure({
aws_cloud_logic_custom: [
{
...amplifyEndpoint,
endpoint: process.env.REACT_APP_API_URL || amplifyEndpoint.endpoint,
},
]
})
const response = await API.post("MyApiNameHere", "/some-endpoint", {data:"here"})
}
This works but a) is this really the correct way to do it? and b) I'm seeing a weird issue whereby the first API.post request of the day from a user is missing the authorization & x-amz-security-token headers that I expect Amplify to be magically providing. If a user refreshes the page, the headers are sent and everything works as expected.
[edit] turns out my missing headers issue is unrelated to this override, so still need to get to the bottom of that!
The more correct place looks to be in the index.js:
import { Amplify, API } from "aws-amplify"
import awsExports from "./aws-exports"
Amplify.configure(awsExports)
const amplifyEndpoint = API._restApi._options.endpoints[0]
Amplify.configure({
aws_cloud_logic_custom: [
{
...amplifyEndpoint,
endpoint: process.env.REACT_APP_API_URL || amplifyEndpoint.endpoint,
},
]
})
That way it's done once - rather than needing to be done in each API call.
Naturally you would need some more complicated logic if you were dealing with multiple endpoints.
I am trying to use RTK Query mutations to upload a file to the API. Here is my mutation code:
addBanner: builder.mutation({
query(body) {
return {
url: `/api/banners`,
method: 'POST',
body,
}
},
})
Here is how I generate the data for request.
const [addBanner, { isBannerLoading }] = useAddBannerMutation();
const new_banner = new FormData();
new_banner.append("file", my_file);
new_banner.append("type", my_type);
new_banner.append("title", my_title);
addBanner(new_banner).unwrap().then( () => ...
But I get an error:
A non-serializable value was detected in the state, in the path: `api.mutations.L-Mje7bYDfyNCC4NcxFD3.originalArgs.file`...
I know I can disable non-serializable check entirely through middleware, but I don't think it is an appropriate way of using Redux Toolkit and RTK. Without a file all works fine. Is there any right way of uploading files with RTK?
Edit: This has been fixed with #reduxjs/toolkit 1.6.1 - please update your package
I just opened an issue for this: https://github.com/reduxjs/redux-toolkit/issues/1239 - thanks for bringing it up!
For now, you'll probably have to disable that check (you can do so for a certain path in the state while keeping it for the rest with the ignoredPath option).
I tried to find how to change the default page path (when you access the server on the "/" path) but couldn't find anything. I have a structure in the pages directory in the form index/index.jsx and I want this page to be returned by default when accessing the server. I did not find a similar question on this forum, maybe someone will need your help, except me.
The way pages structure works in Next.js is very opiniated. If you need to send a file from a different pages structure when accessing / you would need to configure your own little express server.
See this link for official doc.
You would then have something like :
// Some code
if (pathname === '/') {
app.render(req, res, '/index', query) // Or /index/index.jsx
}
// Some code
Also if you do not want to create your own express server, you could have an index.js that redirects to your index page with something like this :
import Router from 'next/router'
const Index = () => null
Index.getInitialProps = async ({ res }) => {
if (res) {
res.writeHead(302, {
Location: `/index`
})
res.end()
}
else
Router.push(`/index`)
return {}
}
export default Index
I'm working with nextjs and this example https://github.com/zeit/next.js/tree/master/examples/with-static-export
in next.config.js i have code:
module.exports = {
async exportPathMap(defaultPathMap, { dev, dir, outDir, distDir, buildId, incremental }) {
// we fetch our list of posts, this allow us to dynamically generate the exported pages
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_limit=3'
)
const postList = await response.json()
// tranform the list of posts into a map of pages with the pathname `/post/:id`
const pages = postList.reduce(
(pages, post) =>
Object.assign({}, pages, {
[`/post/${post.id}`]: { page: '/post/[id]' },
}),
{}
)
// combine the map of post pages with the home
return Object.assign({}, pages, {
'/': { page: '/' },
})
},
}
Its fetch 3 posts and generate files - [id].html - its great!
But now i need to fetch new post and build page only for this new post but commad next export remove all files from out and create only one post.
What i need to do to keep old post and add new one on next export?
Example:
First next export with request for 3 posts from api
generate 3 post in folder "out"
change api url and run next export for 1 new post
summary i have 3 old post pages and 1 new in my "out" directory
How to do that?
Next can't do this out of the box, but you can set it up to do so. First, you'll need a system (database) of which pages have already been built. Second, you'll need some method of communicating with that database (api) to ask which pages should be built (eg, send over a list of pages and the api responds telling you which ones have not yet bene built). Then, tell your exportPathMap which pages to build. And finally, move your built pages out of out and into a new final/public directory.
By default, Next will build/export anything in the pages directory plus anything you set in exportPathMap, and put all of those in the out directory. You can override what it builds by passing a custom exportPathMap, and how you handle what goes into the out directory is up to you, so you can move those files to a different actual public directory and merge them with the old files.
For the URL to which a route applies I have a part defined in settings.json, like this
baseUrl: '/private'
My settings are published and accessible through the collections 'Settings' (on the client). So I tried the following:
Meteor.subscribe('settings');
Deps.autorun(function () {
var settings = Settings.findOne():
if (settings) {
Router.map(function () {
this.route('project', {
path: settings.baseUrl + '/:projectId,
controller: 'ProjectController'
});
});
}
});
The problem is that during initialisation the data is not yet on the client available, so I have to wait until the data is present. So far this approach doesn't work (yet). But before spending many hours I was wondering if someone has done this before or can tell me if this is the right approach ?
Updated answer:
I published solution in repository : https://github.com/parhelium/meteor-so-inject-data-to-html
. Test it by opening url : localhost:3000/test
In this case FastRender package is useless as it injects collection data in the end of head tag -> line 63.
Inject-Initial package injects data in the beginning of head tag -> line 106.
Needed packages:
mrt add iron-router
mrt add inject-initial
Source code:
Settings = new Meteor.Collection("settings");
if (Meteor.isClient) {
var settings = Injected.obj('settings');
console.log(settings);
Router.map(function () {
this.route('postShow', {
path: '/'+settings.path,
action: function () {
console.log("dynamic route !");
}
});
});
}
if (Meteor.isServer){
if(Settings.find().count() == 0){
Settings.insert({path:"test",data:"null"});
}
Inject.obj('settings', Settings.findOne());
}
Read about security in the bottom of the page : https://github.com/gadicc/meteor-inject-initial/
OLD ANSWER :
Below solution won't work in this specific case as FastRender injects data in the end of head tag. Because of that Routes are being initialized before injected data is present.
It will work when data from Settings collection will be sent together with html.
You can do that using package FastRender.
Create file server/router.js :
FastRender.onAllRoutes(function(path) {
// don't subscribe if client is downloading resources
if(/(css|js|html|map)/.test(path)) {
return;
}
this.subscribe('settings');
});
Create also publish function:
Meteor.publish('settings', function () {
return Settings.find({});
});
The above code means that if user open any url of your app then client will subscribe to "settings" publication and data will be injected on the server into html and available for client immediately.
I use this approach to be able to connect many different domains to meteor app and accordingly sent proper data.