Data accesible in template but not in script (VUE 3 + Quasar + Firebase) - firebase

I am currently trying fetch data to populate a form. This is essentially a product edit form so the user can edit his product so I would like the form to be populated with the product's current data. I am fetching the post's data from Firebase with this action:
async fetchEditAuto(did) {
this.editAuto = [];
this.loading = true;
try {
const q = query(autosCollectionRef, where("did", "==", did));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
let autoToEdit = {
did: doc.id,
...doc.data(),
};
this.editAuto.push(autoToEdit);
});
} catch (error) {
this.error = error;
} finally {
this.loading = false;
}
},
this works fine and I get the data, however, I seem to be able to use it only in the template. This is the component's script:
<script setup>
import { ref, computed, reactive } from "vue";
import { useRoute } from "vue-router";
import { useAutosStore } from "stores/autos";
import { storeToRefs } from "pinia";
const { editAuto, loading, error } = storeToRefs(useAutosStore());
const route = useRoute();
const storeAutos = useAutosStore();
const { fetchEditAuto } = useAutosStore();
const id = route.params.did;
fetchEditAuto(id);
const getAutoData = () => {
const autoData = editAuto;
if (autoData == null) {
return null;
}
return autoData[0].nombreVehiculo;
};
getAutoData();
const emit = defineEmits([
"emit:nombreVehiculo",
"emit:fecha",
"emit:transmision",
"emit:tipo",
"emit:potenciaHp",
"emit:kilometraje",
"emit:medida",
"emit:airbags",
"emit:turbo",
"emit:valorVehiculo",
]);
const editFormColumn1Data = reactive({
nombreVehiculo: autoData[0]?.nombreVehiculo,
fecha: "",
transmision: "",
tipo: "",
potenciaHp: "",
kilometraje: "",
medida: "",
airbags: "",
turbo: "",
valorVehiculo: "",
});
I have to be able to populate the editFormColumn1 properties with the the current data but anytime I try to access this data in the script I get undefined, even though it works just fine in the template. What am I doing wrong? Any pointers will be greatly appreciated.

Related

Displaying data from SWAPI

I am trying to display film details from this API https://swapi.dev/api/films in NextJS but I keep getting an error. It has something to do with the getStaticPath function. I am using this code: any help welcome.
import fetch from 'isomorphic-unfetch';
export const getStaticPaths = async () => {
const res = await fetch('https://swapi.dev/api/films');
const data = await res.json();
console.log("This is " + JSON.stringify(data));
const paths = data.results.map(film => {
return {
params: { id: film.episode_id.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch(`https://swapi.dev/api/films` + id);
const data = await res.json();
return {
props: { film: data },
};
};
const Details = ({ film }) => {
return (
<div>
<h1>Episode {film.episode_id}</h1>
<h1>{film.title}</h1>
</div>
);
};
export default Details;
I am expecting the episode ID and title to display
enter image description here
I have tried taking 'results' out of 'data.results.map', so 'data.map' but just results in a error says data.map is not a function...I guess because data is an object not an array. But results is an array so I am still lost. I do think the issue lies here somewhere though...

Empty Vue 3 reactive object on some pages

I have an object that I want to access from all my web app pages. I am using Vue 3 reactivity but I have noted that in some instances, I have to refresh the page to get the object.
How do I refactor my code to have the object always? See my code below:
import { reactive, watch } from "vue";
import getSchoolDocuments from "../composables/getSchoolDocuments";
import { projectAuth } from "../firebase/config";
let user = projectAuth.currentUser;
let userId;
if (!user) {
userId = localStorage.getItem("userId");
} else {
userId = user.uid;
}
const { error: schoolError, documents: schoolDetails } = getSchoolDocuments(
"schools",
userId
);
watch(schoolDetails, (newValue, oldValue) => {
console.log(oldValue);
school.id = newValue["id"];
school.name = newValue["name"];
school.staff = newValue["staff"];
school.vehicles = newValue["vehicles"];
school.contacts = newValue["contacts"];
});
export const school = reactive({
id: "",
name: "",
staff: "",
vehicles: "",
error: schoolError
});

How do I receive promise values in meteor for helper functions?

I wanted to work with Shopify's address library. Since these work with promises I thought about implementing callbacks in order to receive the results
import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict'
import AddressFormatter from '#shopify/address';
import './main.html';
const address = {
company: 'Shopify',
firstName: '恵子',
lastName: '田中',
address1: '八重洲1-5-3',
address2: '',
city: '目黒区',
province: 'JP-13',
zip: '100-8994',
country: 'JP',
phone: '',
};
Template.hello.onCreated(function () {
const addressFormatter = new AddressFormatter('ja');
const instance = this
instance.state = new ReactiveDict()
instance.state.setDefault('result', {
"formattedAddress": "",
"orderedFields": ""
});
getData(addressFormatter, function(r) {
// the next line triggers the helper, since it "observes" the changes
// to this "result" property on the reactive-dictionary
instance.state.set('result', {
formattedAddress: r.formattedAddress,
orderedFields: r.orderedFields
});
});
})
Template.hello.helpers({
address: function() {
console.log(Template.instance().state.get("result"));
return Template.instance().state.get('result')
}
});
function getData(addressFormatter, callback) {
const fa = async () => {
const result = await addressFormatter.format(address);
console.log(result)
return result;
}
const of = async () => {
const promise = addressFormatter.getOrderedFields('CA');
promise.then(result => {
console.log(result);
return result;
});
}
let results = {
"formattedAddress": fa(),
"orderedFields": of()
}
callback(results);
}
The only thing that I receive in the template are [object Promise]. The console.logs in the getData() method actually show the accurate data but they are not displayed in teamplte. What can I do to receive the values and make my helper wait for them?
Edit: I have edited it according to #Jankapunkt answer but the objects are still empty, while the results in getData() are not.
You don't. Helpers are there to immediately return values but are triggered by reactive data sources.
If you want a helper to "run" once the data "arrived" then your should move this code into onCreated and store the value in a reactive data source:
import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict'
import AddressFormatter from '#shopify/address';
import './main.html';
const address = {
company: 'Shopify',
firstName: '恵子',
lastName: '田中',
address1: '八重洲1-5-3',
address2: '',
city: '目黒区',
province: 'JP-13',
zip: '100-8994',
country: 'JP',
phone: '',
};
Template.hello.onCreated(function () {
const instance = this
instance.state = new ReactiveDict()
instance.state.setDefault('result', {
"formattedAddress": "",
"orderedFields": ""
})
const addressFormatter = new AddressFormatter('ja')
getData(addressFormatter)
.then(({ formattedAddress, orderedFields }) => {
// the next line triggers the helper, since it "observes" the changes
// to this "result" property on the reactive-dictionary
instance.state.set('result', { formattedAddress, orderedFields })
})
.catch(e => console.error(e))
return results;
})
Template.hello.helpers({
address: function() {
return Template.instance().state.get('result')
}
});
const getData = async function (addressFormatter) {
const formattedAddress = await addressFormatter.format(address)
const orderedFields = await addressFormatter.getOrderedFields('CA')
return {
formattedAddress,
orderedFields
}
}
Readings: http://blazejs.org/
Edit: added a simplified getData that should work

nextJS SSR useRouter() does not work when refresh page

I am using nextJS SSR in my project. Now when I try to use the following code to get page parameters then it shows undefined.
function About() {
const router = useRouter();
const { plan_id } = router.query;
console.log(plan_id)
}
export default About;
It works when the page is routed from some other page (without page reload with "next/link") but it does not work when I refresh the page. Can someone please help?
I found the answer self. Actually when you refresh the page then the router does not get initialized instantly. So you can add that under UseEffect hook as following and you will be able to get the parameters
function About() {
const [param1, setParam1]=useState("");
const router = useRouter();
useEffect(() => {
if (router && router.query) {
console.log(router.query);
setParam1(router.query.param1);
}
}, [router]);
}
When this router parameter will change then it will call the "UseEffect" which can be used to retrieve the values.
function About({plan_id}) {
console.log(plan_id)
}
// this function only runs on the server by Next.js
export const getServerSideProps = async ({params}) => {
const plan_id = params.plan_id;
return {
props: { plan_id }
}
}
export default About;
You can find more intel in the docs.
I fix this problem with this method.
First add getServerSideProps to your page
//MyPage.js
export async function getServerSideProps({req, query}) {
return {
props: {
initQuery: query
}
}
}
Then created useQuery function like this
//useQuery.js
export let firstQuery = {}
export default function useQuery({slugKey = 'slug', initial = {}} = {}) {
const {query = (initial || firstQuery)} = useRouter()
useEffect(() => {
if (_.isEmpty(initial) || !_.isObject(initial))
return
firstQuery = initial
}, [initial])
return useMemo(() => {
if (!_.isEmpty(query)) {
return query
}
try {
const qs = window.location.search.split('+').join(' ');
const href = window.location.href
const slug = href.substring(href.lastIndexOf('/') + 1).replace(/\?.*/gi, '')
let params = {},
tokens,
re = /[?&]?([^=]+)=([^&]*)/g;
if (slug)
params[slugKey] = slug
while (tokens = re.exec(qs)) {
params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
}
return params
} catch {
}
}, [query])
}
And always use useQuery for receive query params
//MyPage.js
export default function MyPage({initQuery}) {
const query = useQuery({initial: initQuery})
return(
<div>
{query.myParam}
</div>
)
}
And in components like this
//MyComponent.js
export default function MyComponent() {
const query = useQuery()
return(
<div>
{query.myParam}
</div>
)
}
For those still having issues with this. Here is a solution that worked for me
function About() {
const [param1, setParam1]=useState("");
const router = useRouter();
const { param1 } = router.query()
useEffect(() => {
if (!param1) {
return;
}
// use param1
}, [param1]);
}
You can find the solution here

Query data from Dynamo DB using Global secondary index

I am setting a serverless application using AWS Amplify
My frontend app has the following code
import React, { Component } from 'react';
import './App.css';
import Layout from './Containers/Layout';
import { Amplify, API } from 'aws-amplify';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
const apiName = 'top3DynamoDBAPI';
let path = '/listings/';
let partitionKey = 'Restaurant';
class App extends Component {
componentDidMount() {
API.get(apiName, path + partitionKey).then(response => {
console.log(response)
});
}
state = {
listings: {
}
}
render() {
return (
<div className="App">
<Layout />
</div>
);
}
}
export default App;
in my backend API the get method to retrieve items from the table is as follows
/********************************
* HTTP Get method for list objects *
********************************/
app.get(path + hashKeyPath, function(req, res) {
var condition = {}
condition[partitionKeyName] = {
ComparisonOperator: 'EQ'
}
if (userIdPresent && req.apiGateway) {
condition[partitionKeyName]['AttributeValueList'] = [req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH ];
} else {
try {
condition[partitionKeyName]['AttributeValueList'] = [ convertUrlType(req.params[partitionKeyName], partitionKeyType) ];
} catch(err) {
res.statusCode = 500;
res.json({error: 'Wrong column type ' + err});
}
}
let queryParams = {
TableName: tableName,
KeyConditions: condition
}
dynamodb.query(queryParams, (err, data) => {
if (err) {
res.statusCode = 500;
res.json({error: 'Could not load items: ' + err});
} else {
res.json(data.Items);
}
});
});
In my Dynamo DB table, I have a primary partition which has categories and one of them is called 'Restaurant' . So in my App.js I set some variables and call the API to get the items in ComponentDidMount
const apiName = 'top3DynamoDBAPI';
let path = '/listings/';
let partitionKey = 'Restaurant';
componentDidMount() {
API.get(apiName, path + partitionKey).then(response => {
console.log(response)
});
this returns all the items from the table where the primary partition matches a value called 'Restaurant'
Now I have global Secondary Partition called 'Listing_Location'
which currently has two values -- Sydney and Brisbane.
The backend API uses DynamoDB's Document Client and has the following variable initialised
const userIdPresent = false; // TODO: update in case is required to use that definition
const partitionKeyName = "Listing_Category";
const partitionKeyType = "S";
const sortKeyName = "Listing_Id";
const sortKeyType = "S";
const hasSortKey = sortKeyName !== "";
const path = "/listings";
const UNAUTH = 'UNAUTH';
const hashKeyPath = '/:' + partitionKeyName;
const sortKeyPath = hasSortKey ? '/:' + sortKeyName : '';
I am stuck at trying to figure out how to pass the secondary partition to my backend so I can lookup items based on location. Please can you help with this.
I was able to solve it with a combination of info from
DynamoDb how to query a Global Secondary Index? and
https://medium.com/#ole.ersoy/sending-an-email-parameter-with-amplify-api-get-request-4c1c8dc0c952
Now, my App.js looks like
componentDidMount() {
let params = {
'queryStringParameters': {
location: 'Brisbane'
}
}
API.get(apiName, path, params).then(response => {
this.setState({
listings: response
})
console.log(response)
});
}
New get function is
/* NEW GET ATTEMPT*/
app.get(path, function (req, res) {
if (userIdPresent) {
req.body['userId'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
}
const location = req.query.location;
var queryItemParams = {
TableName: tableName,
IndexName: "ListingGSI",
KeyConditionExpression: "#location = :v_location",
ExpressionAttributeNames: {
"#location": "Listing_Location"
},
ExpressionAttributeValues: {
":v_location": location
}
};
dynamodb.query(queryItemParams, (err, data) => {
if (err) {
res.statusCode = 500;
res.json({ error: 'Could not load items: ' + err });
} else {
res.json(data.Items);
}
});
});

Resources