SPA Routing Vanilla JavaScript - javascript

I'm doing a SPA project using Vanilla JS. It is a kind of notice board platform where there are categories. Each category will have a list of posts that users have uploaded.
This is the URL format for each category: base_URL/{category}/{pageNum}.
This is the URL format for each post: base_URL/board/${board_id}.
I have to extract the board_id some how upon the user clicking it and use it in the URL (ex: http://example.com/board/31890adfa).
From searching I found out I'll be using the history API.
Then I'll extract the board_id from the URL and send a request to view that post:
const fetchBoardDetails = async (board_id) => {
await fetch(`http://${base_URL}/board/${board_id}`, {
method: "GET",
headers: { "Content-Type": "application/json" },
}).then(async (res) => {
const data = await res.json();
root.innerHTML = `
<h1>${data.title}</h1>
<ul>
<li>${data.category}</li>
<li>${data.date}</li>
<li>${data.place}</li>
<li>${data.fee}</li>
</ul>
`
}
My problem is, how do I get the board_id in step 1 to use in step 2? I think it has to do with URL routing and I've tried to figure it out but can't wrap my head around it.

Related

Understanding Shopify App Proxy - CORS Policy issue

I am struggling to understand how to use the Shopify App Proxy feature.
We have a store using Shopify Pay and multi currency, but have offsite landing pages on Unbounce and the CRO people want to use direct checkout links, ie
https://our-shopify-store.myshopify.com/cart/36485954240671:1
Which is a nice idea, but the product gets added to the cart and then redirected to checkout in the store default currency, and it needs added in an appropriate currency based on the user location.
There seems to be no way to pass currency as a parameter in the direct checkout links so I am trying to create an intermediary app running on an app proxy to intercept this request;
https://my-cunning-proxy.com/36485954240671:1
And use a 3rd party API to get the user geoip before trying to create a checkout in the user currency by posting the variant, quantity and currency to the checkout API and receiving back a checkout url to redirect the visitor to.
What I've tried
I've got an app working in development.
The app is on https://our-shopify-store.myshopify.com with an app proxy of https://my-cunning-proxy.com/
I can access the app on https://my-cunning-proxy.com?host=our-shopify-store.myshopify.com but if I try to post to the checkout API in an included .jsx file;
const dataBody = "{\"checkout\":{\"line_items\":[{\"variant_id\":7699511831234,\"quantity\":5,\"presentment_currency\": \"USD\"}]}}";
const authtoken = 'xxxxx';
const shopifyDomain = 'https://our-shopify-store.myshopify.com/admin/api/2022-10/checkouts.json';
const response = await axios({
url: shopifyDomain,
method: 'POST',
headers: {
"X-Shopify-Access-Token": authtoken,
"Content-Type": "application/json",
"Access-Control-Allow-Origin":"*"
},
data: {
dataBody
}
});
console.log(response.data);
It rejects the post based on CORS policy. I'm not sure where the Subpath prefix and Subpath even come into things. None of the following urls work, they all 404;
const shopifyDomain = '/admin/api/2022-10/checkouts.json';
const shopifyDomain = 'https://our-shopify-store.myshopify.com/apps/proxy-subpath/admin/api/2022-10/checkouts.json';
const shopifyDomain = 'https://our-shopify-store.myshopify.com/apps/admin/api/2022-10/checkouts.json';
const shopifyDomain = '/apps/proxy-path/admin/api/2022-10/checkouts.json';
Any pointers/suggestions/observations/criticisms much appreciated, it's my first public Shopify app and it has been something of a struggle to even get to this lowly position.
I will inform you that you are indeed using App Proxy in a manner that induces head-scratching. But nonetheless, carry on.
If you wanted to create a checkout with more correctness, you can leverage Storefront API. It is meant to provide you with access to both your products and checkout, without actually forcing your customers to use your Shopify store. You should pivot to trying that out. While it is not perfect, it is far superior to simply hitting a checkout with a variant ID, because as you have indicated, that does not work at all with multi-currency. Or does it?
Since Shopify hacked on languages and markets recently, have you looked into markets, where you can sell to your "market" in their currency?
Anyway, long story short, and the answer, you are getting CORS because you are doing App Proxy all wrong, and no, it cannot help you with your checkout issue!
As mentioned in the comments, I realised the easy solution to the problem (although not necessarily the answer to this question) was to do it using JS and not use an app at all.
Create a custom page template that includes a div <div id="direct-checkout"></div>. In this example it has the url /pages/direct-checkout
Assuming there is a currency switcher form template in the theme in this format;
{%- if localization.available_countries.size > 1 -%}
<div class="currency-switcher-form">
<localization-form>
{% form 'localization' %}
<div class="select-country" data-input-name="country2" data-selected-country="{{ localization.country.iso_code }}"></div>
<input type="hidden" name="country_code" value="{{ localization.country.iso_code }}">
{% endform %}
</localization-form>
</div>
{%- endif -%}
You would use the following JS for both general currency switching (rather than using the badly performing and impossible to tweak Shopify Geolocation App) and also to handle the direct-to-checkout links passing product variant and quantity parameters.
It uses a cookie to reduce the geoip API calls and would redirect users who had previously been on the site to the currency they had previously been assigned/selected.
/*-----------------------------------------------------------------------------------*/
/* GeoIP currency switching
/*-----------------------------------------------------------------------------------*/
function setCurrencyCookie(countryCode) {
var cookieArguments = {
expires: 30,
path: '/',
domain: 'your-domain.com'
}
Cookies.set('_your-domain-geoip-currency', countryCode, cookieArguments);
}
var cookieName = '_your-domain-geoip-currency';
if (!Cookies.get(cookieName)) {
autoCurrencySwitch();
} else {
instantBuy();
}
function autoCurrencySwitch() {
$.ajax( {
url: 'https://api.ipgeolocation.io/',
type: 'GET',
dataType: 'json',
success: function(location) {
var currencyCountry= location.country_code2;
$('input[name="country_code"]').val(currencyCountry);
$(".select-country").attr('data-selected-country', currencyCountry);
var form = $('form#localization_form');
var actionUrl = form.attr('action');
$.ajax({
type: "POST",
url: actionUrl,
data: form.serialize(),
success: function(data){
setCurrencyCookie(currencyCountry);
instantBuy();
},
error: function(errorData){
console.log('currency switch error', errorData);
}
});
}
});
}
/*-----------------------------------------------------------------------------------*/
/* Direct Checkout Redirect
/*-----------------------------------------------------------------------------------*/
function instantBuy( ){
var directCheckoutElement = document.getElementById("direct-checkout");
const params = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
});
// Example URL
// https://your-domain.com/pages/direct-checkout?variant=417239912345&quantity=1
let variantId = params.variant;
let qty = params.quantity;
if (directCheckoutElement && variantId && qty) {
$.ajax({
url: "/cart/add.js",
type: "POST",
data : { id: variantId, quantity: qty },
dataType: 'json',
success: function(data){
window.location.href = "/checkout";
},
error: function(errorData){
console.log('checkout redirect error', errorData);
}
});
} else {
return false;
}
}
Hopefully this can help someone with the same requirement I had.

Stripe Payment element show saved card

I am using laravel with stripe payment element. I am trying to show the saved cards for the customers that we already have. I have followed the stripe docs and found how I can show it on checkout. But the problem is that I am not getting the saved cards for the customer. And instead I am facing an error on my console as:
When authenticating with an ephemeral key, you must set the Stripe-Version header to an explicit API version, such as 2020-08-27
I have checked and changed lot of versions from here:
$ephemeralKey = \Stripe\EphemeralKey::create(
['customer' => "$user->stripe_customer_id"],
['stripe_version' => '2019-11-05']
);
I changed the version to different version that I can see on my stripe dashboard:
This is my Js Initialize function:
// Fetches a payment intent and captures the client secret
async function initialize() {
// Customize the appearance of Elements using the Appearance API.
const appearance = { /* ... */ };
// Enable the skeleton loader UI for the optimal loading experience.
const loader = 'auto';
const { clientSecret, customerOptions } = await fetch("{{ route("user-create-stripe-element-payment") }}", {
method: "POST",
headers: {
"Content-Type" : "application/json",
"accept" : "application/json",
'X-CSRF-TOKEN': "{{ csrf_token() }}",
'stripe_version':"2019-11-05"
},
body: JSON.stringify({ totalCharge:total }),
}).then((r) => r.json());
elements = stripe.elements({
clientSecret,
appearance,
loader,
customerOptions
});
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
}
And I am also using the betas which is given in the documentation:
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
});
But this error is not going away. And its not even populating the Payment element.
Please help me debug this or if someone has any suggestion to check what is going on here.
Thanks in advance.
You are not providing an API version in your JS here
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
});
change the above code to
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
apiVersion: 'Your Version Here'
});
In your case, it should be something like this
const stripe = Stripe("{{env('STRIPE_KEY')}}", {
betas: ['elements_customers_beta_1'],
apiVersion: '2019-11-05'
});
You can read more here. https://stripe.com/docs/api/versioning?lang=node
It is for nodejs but the API version override will work in the same way.

Axios get call in Vue3 not working, although curl and javascript work as expected

I'm trying to make an API call from my Vue3 app. The prepared API has an endpoint like http://localhost:8888/api/dtconfigsearch, where one needs to pass a json payload like { "Modelname": "MyFancyModel"} to get the full dataset with the given modelname. Pure get functions without a payload / a body do work from my Vue3 project to the golang backend, but I'm having problems with passing a payload to the backend.
Test with curl -> ok
$ curl -XGET localhost:8888/api/dtconfigsearch -d '{"Modelname" : "MyFancyModel" }'
{"ID":4,"Modelname":"MyFancyModel","ModelId":"96ee6e80-8d4a-b59a-3524-ced3187ce7144000","OutputTopic":"json/fancyoutput"}
$
This is the expected output.
Test with javascript ok
Source file index.js:
const axios = require('axios');
function makeGetRequest() {
axios.get(
'http://localhost:8888/api/dtconfigsearch',
{
data: { Modelname : "MyFancyModel" },
headers: {
'Content-type' : 'application/json'
}
}
)
.then(resp => {
console.log(resp.data)
})
.catch(err => {
console.log(err)
})
}
makeGetRequest()
Output
$ node index.js
{
ID: 4,
Modelname: 'MyFancyModel',
ModelId: '96ee6e80-8d4a-b59a-3524-ced3187ce7144000',
OutputTopic: 'json/fancyoutput'
}
$
Here, I also get the desired output.
Test within Vue fails :-(
Source in the Vue one file component:
onSelection(event) {
let searchPattern = { Modelname : event.target.value }
console.log(event.target.value)
console.log("searchPattern = " + searchPattern)
axios.get("http://localhost:8888/api/dtconfigsearch",
{
data : { Modelname : "Windshield"},
headers: {
'Content-type' : 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err)
alert("Model with name " + event.target.value + " not found in database")
})
},
Output in browser:
In the image you can see in the terminal log on the right side that the backend is not receiving the body of the API call. However, in the browser information of the call there is content in the config.data part of the object tree, which is the payload / the body. The only thing that bothers me that it is not a json object, but stringified json, although it was entered as json object. According to the documentation, the parameter name (data) in the call should be correct to hold the body content of the api call.
I've tried different header information, looked if it could be a CORS issue, what it isn't to my opinion, exchanged key data with body, used axios instead of axios.get and adapted parameter, all without success. The version of the axios library is 0.27, identical for Vue and vanilla javascript. After checking successfully in javascript, I was sure that it would work the same way in Vue, but it didn't.
Now I'm lost and have no further ideas how to make it work. Maybe someone of you had similar issues and could give me a hint? I'd be very grateful for some tipps!!

Syntax for correct Routes path

So I have a component for displaying players which uses my displayPlayerObject function. The problem is it is I do not have the correct route for fetching in the function and for POST in my routes.rb. The model relationships have the players belonging to the teams and the route for getting a player is "http://localhost:3000/api/teams/1/players/1" 1 being the team id for the former and player id in the latter. But how do I make the displayPlayerObject work the same way syntax wise? And how should it look like for the POST in routes.rb? Also I suspect my players controller's 'show' is wrong as well.
displayPlayerObject function (edited):
export const displayPlayerObject = (id, teamId, type) => {
return dispatch => {
const data = { id };
fetch(`/api/teams/:team_id/players/show`, {
method: 'post',
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(responseJSON => { dispatch({ type , player_object: responseJSON})
})
}
};
My routes.rb (edited):
Rails.application.routes.draw do
scope '/api' do
post "teams/show", to: 'teams#show'
post "teams/:team_id/players/show", to: 'players#show'
resources :teams do
resources :players
resources :star_players
end
end
end
Players Controller Show (edited):
def show
Player.find(params[:id])
render json: #player, status: 200
end
Let's make it look better.
First of all Player.find_by(id: params[:id]) is nonsense, since find_by(id: params[:id]) is equal to where(id: params[:id]).take. Better to replace it with classic find(params[:id]) which looks much better.
What about your main question, what is the point to give name for POST with displayObject. For me display something means GET it to me. If you want to get some player you need to call api/teams/:team_id/players/:id. But if you want to create new player you need to make POST request to /api/teams/:team_id/players route.
P.S.1 Just change request method from POST to GET. And then add to your promise this:
.then(res => {
console.log(res)
})
And observe what server returns
P.S.2 Change request to
fetch(`/api/teams/:team_id/players/:id`)
And in controller find player by :id

Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body in react-admin

I am using react-adminframework, and I have written my own DataProvider. I am trying to accomplish that when an User is created, an instance of UserPossession is created as well. My code bellow accomplishes that, but react-admin Front-end just displays the warning message:
Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body
I checked the Network tab in Developer Tools and every request to server is correct, there is no error. Which leaves me confused and stuck with this, because I have no idea what that warning means or why is it even occuring.
My code is a part of convertDataRequestToHTTP constant and looks like this:
if (resource === 'User') {
url = `${apiUrl}/${resource}`;
options.body = params.data;
httpClient(url, {
method: 'POST',
body: JSON.stringify(options.body),
})
.then(response => (
url = `${apiUrl}/Location`,
options.method = 'POST',
options.body = JSON.stringify({
"odata.type": "HardwareDatabase.UserPossession",
"Name": response.json.Login,
"UserId": response.json.Id
}),
httpClient(url, {
method: options.method,
body: options.body
})
));
}
If you have any questions regarding the code I can clarify.
Thank you for any ideas in advance.
Since you are stating that this code snippet is a part of convertDataRequestToHTTP I might see the issue. httpClient cannot be used in this constant since it creates duplicit calls to API or in your case, this Warning. Correct way would be to only state the options constant.
url = `${apiUrl}/${resource}`;
options.body = JSON.stringifiy(params.data);
options.method = 'POST';
Later in the constant that converts response from OData to mandatory React Admin format, state the httpClient.
params.data = {
"odata.type": "HardwareDatabase.UserPossession",
"Name": response.json.Login,
"UserId": response.json.Id
};
httpClient(`${apiUrl}/Location`, {
method: 'POST',
body: JSON.stringify(params.data),
})
Unfortunately, the GET method for XMLHttpRequest and fetch don't support request bodies.
A temporary work around I found was to use the axios library, which does let you send GET with request bodies.
const res = await axios.get("/api/devices", {
data: { deviceName: 'name' }
})

Categories

Resources