Sveltekit couldn't load and render data from Firestore - firebase

I've been trying to get this sveltekit code to work for the past 1 week and no matter how I tried, it doesn't seem to work.
Before I go to my code, this is the error message:
Cannot read properties of undefined (reading 'length') TypeError: Cannot read properties of undefined (reading 'length')
at Proxy.each (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1710:31)
at eval (/src/lib/components/officer-list.svelte:74:29)
at eval (/src/lib/components/officer-list.svelte:109:5)
at officer-list.svelte:45:12
at Object.$$render (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1745:22)
at eval (/src/lib/sections/training-record.svelte:25:94)
at Object.$$render (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1745:22)
at eval (/src/routes/admin/index.svelte:17:97)
at Object.$$render (/Users/cadellteng/Documents/code/police/APDVolunteers/node_modules/svelte/internal/index.js:1745:22)
at Object.default (root.svelte:43:39)
This is my code to get data from Firebase:
<script lang="ts" context="module">
// Import Firebase
import { db } from '$lib/scripts/firebaseInit'; //from my custom FirebaseInit.ts
import { collection, getDocs } from 'firebase/firestore';
export async function load() {
let allOfficers = [];
console.log("Loading data");
const querySnapshot = await getDocs(collection(db, "officers"));
// console.log(querySnapshot);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
let data = doc.data();
// console.log(doc.id, " => ", data);
allOfficers.push(data);
});
console.log(allOfficers)
return {
props: { allOfficers }
};
}
</script>
<script lang="ts">
export let allOfficers;
</script>
And the following are my code to render the data:
<div class="officer__list">
{#each allOfficers as officer}
<div class="officer__card accordion">
<div class="officer__content-details">
<h4 class="officer-name">{officer.name.displayed}</h4>
<p class="officer-rank-appointment">
Rank 1 | Head of Department
</p>
</div>
</div>
{/each}
</div>
And just in case you are wondering if my FirebaseInit.ts is not working well, I have another example that worked without changing anything in the script tags:
<div class="test-container">
<ul>
{#each allOfficers as officer}
<li>
{officer.name.displayed}
</li>
{/each}
</ul>
</div>
The above works like magic without changing anything in the script tags.
Edit: This is a component within a bigger svelte file. I realize when I make this its own route, it works...? What's wrong?

So I have managed to solve the problem and I can't use the context module to load data at the component level. After I move the script tag content for the load to the route level, it works perfectly. I just need to pass it down into individual components as props.

Related

how to fix nextjs prerender error - vercel deployment?

How do I troubleshoot this problem this deployment issue? I am following this tutorial. My node_modules and .next are ignored and not pushed to github. It works locally but can't seem to deploy. I have supplied both the component code as well as the page it's exported on. Let me know if you can see what I am missing.
https://www.youtube.com/watch?v=V4SVNleMitE
deployment errors
Error occurred prerendering page "/components/BlogPosts". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: Cannot read property 'fields' of undefined
at BlogPosts (/vercel/path0/.next/server/chunks/130.js:39:12)
at d (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:33:498)
at bb (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:16)
at a.b.render (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:42:43)
at a.b.read (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:41:83)
at Object.exports.renderToString (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:52:138)
at Object.renderPage (/vercel/path0/node_modules/next/dist/server/render.js:673:46)
at Object.defaultGetInitialProps (/vercel/path0/node_modules/next/dist/server/render.js:315:51)
at Function.getInitialProps (/vercel/path0/.next/server/pages/_document.js:645:16)
at Object.loadGetInitialProps (/vercel/path0/node_modules/next/dist/shared/lib/utils.js:69:29)
component blog posts
export default function BlogPosts({post }) {
const {title, information,slug , thumbnail} = post.fields
return (
<div>
<div className='container w-50 h-25 mt-4'>
<Image
className='nav'
src={'https:' + thumbnail.fields.file.url}
width={thumbnail.fields.file.details.image.width}
height={thumbnail.fields.file.details.image.height}
/>
</div>
<div>
<div>
<h4 className=''>{title}</h4>
<Link href={'/contentslug/' + slug}>
<a className='btn btn-primary text-white'>Read more</a>
</Link>
</div>
</div>
</div>
)
}
Pages/Posts
import {createClient} from 'contentful'
import BlogPosts from './components/BlogPosts'
import Nav from './components/Nav'
import Logo from './components/Logo'
export async function getStaticProps() {
const client = createClient({
space: process.env.NEXT_PUBLIC_CONTENTFUL_ID,
accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_TOKEN,
})
const res = await client.getEntries({content_type: 'posts'})
return {
props: {
posts: res.items ,
revalidate: 1
}
}
}
export default function Home({posts}) {
console.log(posts);
return (
<div>
<Logo/>
<Nav/>
<div className="container text-center display-5">
{posts.map(post => (
<BlogPosts key={post.sys.id} post={post}/>
))}
</div>
</div>
)
}
You have fields of undefined. this might be caused because of some strange deploying behavior if you are 100% sure your code works.
How to fix (probably):
Build your project locally. if it works, follow the next step
Comment your code in BlogPosts, inside the exported component. The code must work, so your exported component will be empty but working.
Push this code to Vercel.
Uncommit your code. (done at point 2)
Push again.
P.S. this behavior with API is sometimes caused because of API middleware you reworked.

Pushing data to object in different component using POST

TL;DR I want to show submitted posts instantly instead of having to refresh my page
Using the Wordpress REST API I am able to create a new post without any issue. The post is being displayed as soon as the page refreshes, so what I want to do is update the posts object in my Hello.vue file as soon as I create that post so I don't need to refresh to show my newest posts.
I'm not really sure where to start - I've removed all of the experiments I've done so far (importing Post in Create, defining props, pushing to an array, reading about object reactivity on the official Vue documentation, nothing helped).
My App.js consists of the <router> object which shows Hello.vue and a component called Create which displays the Create.vue component. This is how my app currently looks like:
My App.vue file:
<template>
<div id="app">
<section class="posts">
<router-view></router-view>
<create></create>
</section>
</div>
</template>
<script>
import Create from '#/components/Create.vue'
export default {
name: 'app',
components: {
Create
}
}
</script>
<style lang="scss">
#import '../src/assets/styles/style.scss'
</style>
My Hello.vue which displays all the posts:
<template>
<div>
<section class="posts__Feed">
<ul class="posts__List">
<post v-for="item in posts" :item="item" :key="item.id"></post>
</ul>
</section>
</div>
</template>
<script>
var postsUrl = '/wp-json/wp/v2/posts/'
import Post from '#/components/Post.vue'
export default {
name: 'hello',
props: ['responseData'],
components: {
Post
},
data () {
return {
posts: []
}
},
beforeCreate () {
this.$http.get(postsUrl).then((response) => {
this.posts = response.data
})
}
}
</script>
And finally, the Create.vue file which creates the post:
<template>
<div>
<section class="posts__Create">
<form class="posts__CreateForm" v-on:submit="createPosts">
<div class="posts__CreateFormWrapper" v-bind:class="{ 'is-Loading': loading }">
<p>
<input v-model="formInfo.title" type="text" name="title" id="title" placeholder="Name" :disabled="formSent">
</p>
<p>
<textarea v-model="formInfo.content" name="content" id="content" cols="20" rows="10" maxlength="140" placeholder="Message" :disabled="formSent"></textarea>
</p>
<p>
<button :disabled="formSent">Send</button>
</p>
</div>
</form>
</section>
</div>
</template>
<script>
var postsUrl = '/wp-json/wp/v2/posts/'
export default {
name: 'create',
data () {
return {
formInfo: [],
responseData: [],
loading: false,
formSent: false
}
},
methods: {
createPosts (e) {
e.preventDefault()
var info = this.formInfo
// Check if fields are empty
if (this.formInfo.title && this.formInfo.content) {
this.loading = true
// POST
this.$http.post(postsUrl, info).then((response) => {
this.formSent = true
this.loading = false
// get body data
this.responseData = response.data
})
}
} // EOF createPosts
}
}
</script>
Any help would be much appreciated!
I ended up using an event bus as suggested by wotex. First, I've createad a file called bus.js with the below code:
import Vue from 'vue'
export const EventBus = new Vue()
Next, import bus.js to both .vue layouts using:
import { EventBus } from '#/bus.js'
Now emit the event as soon as a new post is created (this is sitting in my axios POST request inside the Create.vue file):
EventBus.$emit('newPost', this.responseData)
And finally, check if the event has happened on the other end (my Hello.vue file):
EventBus.$on('newPost', function (postData) {
Thanks for pointing me in the right direction!

Meteor, Flow Router, and React: How to get FlowRouter.subsReady() function to be reactive in render

I am trying to create a pattern so that all the subscriptions are ready before I load the main page. Similar to Iron Router waitOn.
Take a look at this react component:
export const PageContainer = React.createClass({
render() {
return (
<div id="content-box">
<div className="banner banner-primary">
<div className="page_title pull-left">
{this.props.pageName}
</div>
</div>
<div>
{ FlowRouter.subsReady() ? this.props.page : (
<div> Loading .... </div>
)
}
</div>
</div>
);
}
});
as you can see I am using the FlowRouter.subsReady() helper to render the page or the loading text.
The problem is that this is not reactive. It just renders once but does not update and show the page once the subscription is ready.
How can I get this to be reactive?
What is the best way to use Flow Router's subscription management with React. I have a base layout and want to show loading sign before loading the page main. If I could get this function to be reactive it should work just fine.
UPDATE:
It seems like I have to attach the helper, FlowRouter.subsReady() to the get Meteor data function
export const PageContainer = React.createClass({
mixins: [ ReactMeteorData ],
getMeteorData() {
return {
isLoading: FlowRouter.subsReady()
}
},
render() {
return (
<div id="content-box">
<div className="banner banner-primary">
<div className="page_title pull-left">
{this.props.pageName}
</div>
<i className="fa fa-question-circle help-icon pull-right"></i>
</div>
<div>
{ this.data.isLoading ? this.props.page : (
<div> Loading ... </div>
)
}
</div>
</div>
);
}
});
It seems to be working now. Is this the way to do it?
You accessed the problem in the wrong direction. You don't really need to check the subsReady of FlowRouter when using meteor with react. Just install the mixin ReactMeteorData and set the this.data properly, it will reactively render the Dom. More details here
React render is not reactive. The Dom is only re-rendered when the props or state of the component is changed

Accessing Firebase from Polymer Element

I am working on an app that uses Polymer and Firebase. I am trying to figure out how to access my Firebase document from my custom element. Currently, I have a view defined like this:
<body>
<template is="dom-bind" id="dialog">
<firebase-document location="https://my-app.firebaseio.com/" data="{{ orders }}"></firebase-document>
<iron-pages attr-for-selected="data-route" selected="{{route}}">
<section data-route="home">
<h3>Hello</h3>
</section>
<section data-route="orders">
<h3>Orders</h3>
<user-orders orders="{{ orders }}"></user-orders>
</section>
</iron-pages>
</template>
</body>
The user-orders element is defined like this:
<dom-module id="user-orders">
<template>
<template is="dom-repeat" items="[[ orders ]]" as="order">
<div>[[ order.date ]]</div>
<div>[[ order.status ]]</div>
<div>[[ order.description ]]</div>
</template>
<button on-click="test">Test</button>
<button>Add New Order</button>
</template>
<script>
Polymer({
is: "user-orders",
properties: {
orders: {
type: Array,
notify: true,
value: function() {
return [];
}
}
},
test: function() {
alert(JSON.stringify(this.orders));
}
})
</script>
</dom-module>
The view associated with my route appears correctly. However, when I click my "Test" button, an alert window appears that says null. Its like either a) I've setup my firebase connection improperly, which I'm not sure how to actually confirm or b) My data binding is setup improperly.
What am I doing wrong?
Are you working with a brand new Firebase app or an existing Firebase app? A brand new Firebase app without any data will return null.

passing values to meteor partials

I'm learning meteor to build quick website prototypes.
I'm trying to understand how to generate a set of values to populate the site templates and partials.
I have a layout.html template
<template name="layout">
<div class="container">
<header role="banner">
{{>site-header}}
</header>
<h1>This is {{siteLogo}}</h1>
<main role="main">
{{ yield }}
</main>
<footer role="contentinfo">
{{> site-footer }}
</footer>
</div>
</template>
in main.js I define the following:
Meteor.startup(function(){
Session.set('siteLogo', 'the logo');
});
Template.site-header.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
Template.layout.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
With this i can pass the value of siteLogo to layout.html.
I have a site-header.html partial
<template name="site-header">
<h1>{{siteLogo}}</h1>
</template>
I can't seem to be able to pass the value of siteLogo to the partial. Is there a way to do that?
Is it necessary to create a Session variable to pre-fill some values or can i just create a json settings list and access the value globally?
something that would go in main.js, like the yaml config file in a jekyll site:
siteSettings = [
{
siteLogo: "some brand name",
otherValue: "something else"
}
]
update
I'm a bit confused, I'm must be doing something wrong.
I've created a quick new meteor app to test this.
I have main.html
<head>
<title>handlebar-helper</title>
</head>
<body>
{{> header}}
{{> hello}}
{{> footer}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
<template name="header">
<header>
<h1>{{ headline }}</h1>
<p>tagline</p>
</header>
</template>
<template name="footer">
<footer role="contentinfo">
<h1>{{ headline }}</h1>
<small>copyright</small>
</footer>
</template>
And main.js
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to handlebar-helper.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
Meteor.startup(function(){
Session.set('headline', 'My fancy headline');
});
Handlebars.registerHelper('headline', function(){
return Session.get('headline');
});
}
if (Meteor.isServer) {
// server code here
}
And i can't still pass the value of headline into >header of >footer
if I try to put the Session.set into the Meteor.isServer block, I get a syntax error, Session is not defined
Cheers
Do you have a Template.site-header.helpers function declared for siteLogo? If not it won't work - you can't use a helper from another template. If you need to use siteLogo in a variety of places, it's best to use a Handlebars block helper, as these can be accessed by any template.
UPDATE
The Handlebars helper would just look like this:
Handlebars.registerHelper('siteLogo', function() {
return Session.get('siteLogo');
});
However, if you've already got a siteLogo helper in the site-header Template, it suggests something else is wrong, like a typo in a template or helper name. Is there an error in the console?
UPDATE 2
If you want to use a dictionary-style structure to store reactive data, you can do something like this:
Session.set('myDict', {foo: 1, bar: 2});
Handlebars.registerHelper('myDict', function(key) {
return Session.get('myDict') ? Session.get('myDict')[key] : null;
});
And then use this in your template: {{myDict 'foo'}}. Obviously, the format above would work fine in a tempate helper as well, but it would only be accessible from within that template. The ternary operator is just to check that myDict has been initialised before it lets a template try to look up one of the keys, which is a common Meteor problem on page load.
Incidentally, if you're finding Session variables a cumbersome way to deal with reactive dictionary-like data structures, it's pretty easy to roll your own. This is the best introduction.

Resources