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";
Related
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');
}
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.
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'
I am trying to use Ionic2 and I made a service to fetch a local stored Json.
import {Injectable} from 'angular2/core';
import {Http, Response} from 'angular2/http';
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/map';
#Injectable()
export class Page1Service {
public constructor(private _http: Http) {}
public GetItems() {
return this._http.get('/app/Ressources/Items.json').map((response: Response) => response.json().data);
}
public PrintJson():boolean {
var myresult;
this.GetItems().subscribe((result) => {
myresult = result;
console.log(result);
});
}
I also a made PrintJson() method that just print the json for test purpose.I got the error:
GET http://localhost:8100/app/Ressources/slides.json 404 (Not Found)
I don't get why. And I can't find an easy and uptodate tutorial. Or should I use fetch()?
First copy your json to the following dir(you can create the folder "data"):
[appname]/www/data/data.json
Type in the following command in your console:
ionic g provider JsonData
It should create a provider for you.Go to that page and enter the following in load() function:
load() {
if (this.data) {
// already loaded data
return Promise.resolve(this.data);
}
// don't have the data yet
return new Promise(resolve => {
// We're using Angular Http provider to request the data,
// then on the response it'll map the JSON data to a parsed JS object.
// Next we process the data and resolve the promise with the new data.
this.http.get('data/data.json').subscribe(res => {
// we've got back the raw data, now generate the core schedule data
// and save the data for later reference
this.data = res.json();
resolve(this.data);
console.log(this.data);
});
});
}
I usually create an Observable wrapped around the api-call like this:
public GetItems() {
return Observable.create(observer => {
this._http.get('/app/Ressources/Items.json').map(res =>res.json()).subscribe(data=>{
observer.next(data)
observer.complete();
});
});
}
Then I have to subscribe on that method in order to get the results and do something with it. (You could be to delegate the result to a list in the GUI)
GetItems().subscribe(data=>{
myResult = data;
});
EDIT: It might help to put this in the class as well
export class MyClass{
static get parameters(){
return [[Http]];
}
}
Just try to get the response.json() rather than response.json().data in GetItems() method
The issue is because of different paths of json files in local browser(computer) and device (android). Create data folder inside the src\assets folder. Move your json file into that.
When we run ionic serve, it will move that folder (with file) into www\assets folder. Then do following things:
Import Platform service of ionic2
import { Platform } from 'ionic-angular';
Inject Platform Service.
constructor(private http: Http, private platform: Platform ) { }
Use Platform Service.
public getItems() {
var url = 'assets/data/Items.json';
if (this.platform.is('cordova') && this.platform.is('android')) {
url = "/android_asset/www/" + url;
}
return this.http.get(url)
.map((res) => {
return res.json()
});
}
I would like to retrieve the current params outside of a component, and as far as I can tell React Router does not provide a convenient way of doing that.
Sometime back before 0.13 the router had getCurrentParams() which is what I used to use.
Now the best thing I can figure out is:
// Copy and past contents of PatternUtils into my project
var PatternUtils = require('<copy of PatternUtils.js>')
const { remainingPathname, paramNames, paramValues } =
PatternUtils.matchPattern(
"<copy of path pattern with params I am interested in>",
window.location.pathname);
Is there a way to do this with React router?
you could use matchPath:
import { matchPath } from 'react-router'
const { params }= matchPath(window.location.pathname, {
path: "<copy of path pattern with params I am interested in>"
})
If you use matchPath(window.location.pathname, ...) within your render function, your component won't signal to be re-rendered on route changes. You can instead use react router's useLocation hook to fix this:
import { matchPath } from 'react-router'
import { useLocation } from 'react-router-dom'
function useParams(path) {
const { pathname } = useLocation()
const match = matchPath(pathname, { path })
return match?.params || {}
}
function MyComponent() {
const { id } = useParams('/pages/:id')
return <>Updates on route change: {id}</>
}
Note: matchPath will return null for paths which don't match.
If you use the object destructure pattern { params } = matchPath it may throw the following error:
Cannot destructure property 'params' of 'Object(...)(...)' as it is null.