How to store product-info into session in WooCommerce using Next.js and headless wordpress - json

I am currently working on a Next.js project. As Iā€™m building an online-shop, I also want to create a shopping-cart. I use wordpress (headless) via graphql and the api of WooCommerce.
Now the idea is to somehow store the product information into a session or using the local storage. I think storing the product id would be sufficient, because I want to sell online products that have an unlimited quantity and each customer can only download the product once.
What do I have to do in order that WooCommerce understands, what I want to achieve?
Is it possible instead of using an endpoint to just use the product-info from a JSON? (This is probably not so secure, but as I am only testing. It should be fine)
I have read that also cookies have to be written in order for WooCommerce to work and save the selected product into the shopping-cart.
I would be very thankful for any help šŸ˜Š
Down below I show you part of my current state...
main file for the cart:
import { getSession, storeSession } from './session';
import { getAddOrViewCartConfig } from './api';
import axios from 'axios';
import { ADD_TO_CART_ENDPOINT } from '../constants/endpoints';
import { isEmpty } from 'lodash';
export const addToCart = (productId: int, qty: int = 1) => {
const storedSession = getSession();
const addOrViewCartConfig = getAddOrViewCartConfig();
axios.post(
ADD_TO_CART_ENDPOINT, //instead of using an endpoint, I would like to pull the desired info from a JSON file
data: {
product_id: productId,
quantity: qty,
},
addOrViewCartConfig
)
.then((res: AxiosResponse<any>) => {
if (!isEmpty(storedSession)) {
storeSession(res?.headers?.['x-wc-session']);
}
viewCart();
})
.catch((err) => {
console.log('err', err);
});
};
getAddOrViewCartConfig from api:
import { getSession } from './session';
import { isEmpty } from 'lodash';
export const getAddOrViewCartConfig = () => {
const config = {
headers: {
'X-Headless-CMS': true,
},
};
const storedSession = getSession();
if (!isEmpty(storedSession)) {
config.headers['x-wc-session'] = storedSession;
}
return config;
};
getSession, storeSession from session
import { isEmpty } from "lodash";
export const storeSession = (session) => {
if(isEmpty(session)){
return null;
}
localStorage.setItem('x-wc-session',session);
}
export const getSession = ()=>{
return localStorage.getItem(key:'x-wc-session');
}

Related

Accessing Vuex Store Before Page Load NuxtJS

Context: I am trying to get Google Maps place data via the place_id on the beforeEnter() route guard. Essentially, I want the data to load when someone enters the url exactly www.example.com/place/{place_id}. Currently, everything works directly when I use my autocomplete input and then enter the route but it does not work when I directly access the url from a fresh tab. I've been able to solve this using the beforeEnter() route guard in traditional Vue, but cannot solve for this using Nuxt. Please help!
Question: How can I access the Vuex Store before a page loads in Nuxt?
Error: Any solution I try (see below) I either end up with a blank page or the page will not load (I think it is stuck in a loop and cannot resolve the Promise).
Attempted Solutions:
Using Middleware like below:
middleware({ store, params }) {
return store.dispatch('myModule/fetchLocation', params.id)
}
Using asyncData like below:
data(){
return{
filteredLocation: {}
}
}
// snip
async asyncData({ store, params }) {
const { data } = await store.dispatch('myModule/fetchLocation', params.id)
return filteredLocation = data
}
I tried looking into fetch, but apparently you no longer have access to context
Example Code:
In one of my store modules:
/* global google */
import Vue from 'vue'
import * as VueGoogleMaps from '~/node_modules/vue2-google-maps/src/main'
Vue.use(VueGoogleMaps, {
load: {
key: process.env.VUE_APP_GMAP_KEY,
libraries: 'geometry,drawing,places'
}
})
export const state = () => ({
selectedLocation: {}
})
export const actions = {
fetchLocation({ commit }, params) {
return new Promise((resolve) => {
Vue.$gmapApiPromiseLazy().then(() => {
const request = {
placeId: params,
fields: [
'name',
'rating',
'formatted_phone_number',
'geometry',
'place_id',
'website',
'review',
'user_ratings_total',
'photo',
'vicinity',
'price_level'
]
}
const service = new google.maps.places.PlacesService(
document.createElement('div')
)
service.getDetails(request, function(place, status) {
if (status === 'OK') {
commit('SET_PLACE', place)
resolve()
}
})
})
})
}
}
export const mutations = {
SET_PLACE: (state, selection) => {
state.selectedInstructor = selection
}
}
EDIT: I already have it in a plugin named google-maps.js and in my nuxt.config.js file I have:
plugins: [
{ src: '~/plugins/google-maps.js' }
]
//
//
build: {
transpile: [/^vue2-google-maps.js($|\/)/],
extend(config, ctx) {}
}
Using Middleware is how we can access Vuex before page loads. try putting the configuration part in a custom Nuxt plugin.
Create a file in Plugins folder (you can name it global.js).
Put this
import Vue from 'vue'
import * as VueGoogleMaps from '~/node_modules/vue2-google-maps/src/main'
Vue.use(VueGoogleMaps, {
load: {
key: process.env.VUE_APP_GMAP_KEY,
libraries: 'geometry,drawing,places'
}
})
in global.js.
Then add the plugin in nuxt.config.js like this.
plugins: [
'~/plugins/global.js'
]
Also, make sure you're using underscore before 'page_id' name in your folder structure.

Angular AoT build import local json file

In my first angular app I have a service who import a json file to load some data (I need to load it synchronously, before the DOM).
In develop mode, when I modify the json file, the cli rebuild the app and all work like a charm.
Unluckily on build -prod modifying the json in my 'dist/assets' directory does not update the app. The compiler embed json into my main-es2015.js and do not reference anymore on external file.
works.service.ts:
import { Injectable } from '#angular/core';
import PICTURES from '../../assets/pictures.json';
#Injectable({
providedIn: 'root'
})
export class WorksService {
pictures: any;
constructor() {
this.pictures = PICTURES
}
getWorks() { return this.pictures; }
getSingleWork(id: string) {
return this.pictures.find(i => i.id === id);}
getPreviousWork(id: string) {
const index = this.pictures.findIndex(i => i.id === id) - 1;
if (typeof this.pictures[index] === 'undefined') {
// cycle to start
return this.pictures[this.pictures.length-1]
}
else {
return this.pictures[index]
}
}
getNextWork(id: string) {
const index = this.pictures.findIndex(i => i.id === id) + 1;
if (typeof this.pictures[index] === 'undefined') {
// cycle to start
return this.pictures[0]
}
else {
return this.pictures[index]
}
}
}
I tried to use httpClient or to load the json dynamically:
this.pictures = import('../../assets/pictures.json')
but the page is loaded before the file and I cant figure out how to load it before.
It will be better to create a new pictures.ts file and put it into this file your JSON as an object.
// pictures.ts
export const pictures: any = { "name":"John", "age":30, "car":null };
after that import this const and use it into your components.
// pictures.component.ts
import { pictures } from "./pictures";

How to pass const to multiple components / Spliting React-Redux-Router files

I am creating a Spotify app with its API. I want 4 views (like '/', 'nowPlaying', 'favouriteArtists', 'favouriteSongs').
I need to setAccessToken for using functions like getMyCurrentPlaybackState() in every new page, right?. Idk if I need to if(params.access_token){spotifyWebApi.setAccessToken(params.access_token)} in every container that will use functions like getMyCurrentPlaybackState(). I was thinking of creating a Spotify.jsx container that handle the store of the Spotify Object (which is used in the token and in every container that use spotify functions). But with this Spotify.jsx i don't know either if it is a good approach nor how to connect its needed spotifyWebApi const to every container file and token file.
For better understanding of my idea: I would create a Token.jsx that has getHashParams() and a Playing.jsx that has getNowPlaying(). Every one needs the spotifyWebApi const.
import React, { Component } from 'react';
import Spotify from 'spotify-web-api-js';
const spotifyWebApi = new Spotify();
class App extends Component {
constructor(){
super();
const params = this.getHashParams();
this.state = {
loggedIn: params.access_token ? true : false,
nowPlaying: {
name: 'Not Checked',
image: ''
}
}
if (params.access_token){
spotifyWebApi.setAccessToken(params.access_token)
}
}
getHashParams() {
var hashParams = {};
var e, r = /([^&;=]+)=?([^&;]*)/g,
q = window.location.hash.substring(1);
while ( e = r.exec(q)) {
hashParams[e[1]] = decodeURIComponent(e[2]);
}
return hashParams;
}
getNowPlaying(){
spotifyWebApi.getMyCurrentPlaybackState()
.then((response) => {
this.setState({
nowPlaying: {
name: response.item.name,
image: response.item.album.images[0].url
}
})
})
}
}
Your title mentions Redux, but I don't see your code utilizing it. With Redux, you could get the access_token and then store it in state. This will allow you to use it in any Redux connected component.
Also, with Redux, you can use Redux Thunk (or similar) middleware that will allow you to use Redux actions to call an API. So then you would just write the different API calls as Redux actions, which would allow you to call them from any component, and have the results added to your Redux store (which again, can be used in any Redux connected component).
So, for example, your getNowPlaying() function could be an action looking something like this:
function getNowPlaying() {
return function (dispatch, getState) {
// get the token and init the api
const access_token = getState().spotify.access_token
spotifyWebApi.setAccessToken(access_token)
return spotifyWebApi.getMyCurrentPlaybackState().then((response) => {
dispatch({
type: 'SET_NOW_PLAYING',
name: response.item.name,
image: response.item.album.images[0].url
})
})
}
}
Note: You'll need to configure the Redux reducer for "spotify" (or however you want to structure your store) to store the data you need.
So, you could then call getNowPlaying() from any component. It stores the results in the redux store, which you could also use from any connected component. And you can use the same technique of getting the access_token from the store when needed in the actions.
Alternatively, if you didn't want to use Redux, you could provide context values to all child components, using React's Context features. You could do this with that token that each component would need in your setup. But Redux, in my opinion, is the better option for you here.
Instead of passing this const to other components, I would create a SpotifyUtils.jsx and inside it declare the const. And in this helper file I would export functions so other components can use them.
For example:
import Spotify from 'spotify-web-api-js';
const spotifyWebApi = new Spotify();
let token = null
export function isLoggedIn() {
return !!token
}
export function setAccessToke(_token) {
token = _token;
spotifyWebApi.setAccessToken(_token);
}
export function getNowPlaying(){
return spotifyWebApi.getMyCurrentPlaybackState()
.then((response) => {
return {
name: response.item.name,
image: response.item.album.images[0].url
}
})
}
So that in the components you can use them like so:
import React, { Component } from 'react';
import {
isLoggedIn,
setAccessToken,
getNowPlaying,
} from 'helpers/SpotifyUtils'
class App extends Component {
constructor(){
super();
this.state = {
loggedIn: isLoggedIn(),
nowPlaying: {
name: 'Not Checked',
image: ''
}
}
getHashParams() {
var hashParams = {};
var e, r = /([^&;=]+)=?([^&;]*)/g,
q = window.location.hash.substring(1);
while ( e = r.exec(q)) {
hashParams[e[1]] = decodeURIComponent(e[2]);
}
return hashParams;
}
componentDidMount() {
if (!this.state.loggedIn) {
const params = this.getHashParams();
if (params.access_token) {
setAccessToken(params.access_token)
getNowPlaying()
.then(nowPlaying => this.setState({ nowPlaying }))
}
}
}
}
This will enable your spotifyWebApi const to be reused in any component you import the helper functions. I am particularly found of this pattern, creating utils or helpers in a generic fashion so that you can reuse code easily. Also if spotify Web Api releases a breaking change, your refactor will be easier because you will only need to refactor the SpotifyUtils.jsx file since it will be the only file using import Spotify from 'spotify-web-api-js'

display data from json object on HTML in angular 5

hello i want to display the data that i got from a mongodb using a backend api (nodejs)
this is the code for event model
const mongoose = require('mongoose');
const config = require('../config/database');
// Events Schema
const EventSchema = mongoose.Schema({
eventname: {
type: String,
required: true
},
eventstartdate: {
type: String,
required: true
},
eventenddate: {
type: String,
required: true
},
eventcategorie: {
type: String
},
eventdescription: {
type: String
},
eventimage: {
type: String
}
});
const Event = module.exports = mongoose.model('Event', EventSchema);
this is the code from the router
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const config = require ('../config/database');
const User = require('../models/user');
const Event = require('../models/event');
//get event by id
router.get('/event/:eventid', (req,res) => {
Event.findById(req.params.eventid, (err, event) =>{
if (err){
return res.status(500).send({message:err.message});
}
if(!event){
return res.status(400).send({message:'Event not found'});
}
res.json({
event: {
id: event._id,
eventname: event.eventname,
eventstartdate: event.eventstartdate,
eventenddate: event.eventenddate,
eventcategorie: event.eventcategorie,
eventdescription: event.eventdescription,
eventimage: event.eventimage
}
});
});
});
and this is the code from the service in the angular
// GET an event by ID
displayEvent$(id: string) {
return this.http.get(`http://localhost:3000/users/event/${id}`)
.map(response => response.json());
}
then i created a simple method that is triggered by a button
and i passed an id of an event that i konw is in the database just to test it out
onclickeventpage(){
this.authService.displayEvent$('5ae0c8e96b40a71cd3b772cc').subscribe(event => {
console.log(event)
});
}
this gives me back at the console the event i need with every aribute
but whene i change this
console.log(event)
to this so i can get evey atribute separetly and then i an put them in the html
console.log(event.eventname)
i get undefined
i just want to know how to get every event atribute so i can display them in my html page
First you dont have to call .json() witn angular5
displayEvent$(id: string) {
return this.http.get(`http://localhost:3000/users/event/${id}`)
.map(response => response.json());
}
also you need to access
console.log(event.event.eventname);
HttpModule is deprecated and the new HttpClientModule by default formats the response to JSON so we no longer need to parse it using response.json():
I just want to know how to get every event attribute so that I can
display them on my HTML page
You can tell HttpClient the type of the response to make consuming the output easier and more obvious.
Typechecking of response can be done by using type parameter
export interface Ievent {
id:string
eventname: string
eventstartdate: string
eventenddate: string
eventcategorie: string
eventdescription: string
eventimage: string
}
Http returns an observable and We can tell the HttpClient.get to return response as Ievent type When we use http.get<Ievent>(...) then it returns the instance of Observable<Ievent> type.
In your service
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import {Ievent} from './eventModel'
#Injectable()
export class authService()
{
constructor(private http:HttpClient){}
displayEvent$(id: string)Observable<Ievent> {
return this.http.get<Ievent>(`http://localhost:3000/users/event/${id}`);
}
}
In your component subscribe to Observable<Ievent> to get instance of Ievent
onclickeventpage(){
this.authService.displayEvent$('5ae0c8e96b40a71cd3b772cc').subscribe(event => {
console.log(event);
console.log(event.eventname)});
}

How to create ionic 2 infinite scroll with data from database

I'd query about 500 users from database who are active. I am using laravel and ionic framework.
Here is my query using laravel.
public function getUsers(Request $request) {
$users = DB::table('users')->where('status', $request->status)->get();
return Response::json($users);
}
Here might be my .ts code in ionic 2
import { Component } from '#angular/core';
import { Http, Headers, RequestOptions } from "#angular/http";
#Component({
selector: 'page-users',
templateUrl: 'users.html'
})
export class UsersPage {
users: any = [];
constructor(
private http: Http
) {
var headers = new Headers();
headers.append("Accept", 'application/json');
headers.append('Content-Type', 'application/json' );
let options = new RequestOptions({ headers: headers });
let data = { 'status': 'active'}
this.http.post('http://path/to/laravel/api/getUsers', data, options)
.subscribe(res => {
var jsonData = JSON.parse(res['_body']);
this.users = jsonData;
})
}
doInfinite(infiniteScroll) {
// How can I do infinite scroll here???
setTimeout(() => {
infiniteScroll.complete();
}, 1000);
}
}
I am able to get the 500 active users and display it on my view.
However, It is a kind of laggy because there are too many data query at once.
I want to create infinite scroll to optimize the query. But, I don't know how to implement that in ionic 2.
I want atleast 10 users to be query everytime I scroll. Answers are appreciated.
I'll just pull off one of my codes and replace some variable names. Hopefully it will make sense and be helpfull:
Assuming you have this on your html:
<ion-infinite-scroll (ionInfinite)="fetchMore($event)">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
You will need something like this on your .ts:
fetchMore(event) {
this.myService.getMoreItems(10/*number of items to fetch*/,this.collection.length/*you might need to send the "skip" value*/).then(
(moreItems: any[]) => {
if (moreItems.length > 0) {
this.collection = this.collection.concat(moreItems);
}
event.complete();
},
(err) => {
event.complete();
}
);
}
The myService.getMoreItems part will be your service/provider that has the function, return type is a Promise<any>, that will make the Http Request. I think you got the idea.