Programmatically Set an href Link in React - javascript

I have a product that I want to programatically set a Checkout Session URL and then redirect a user when they click on an tag.
const PlanComponent = () => {
const [tiers, setTiers] = useState([]);
async function CheckoutSessionUrl(tierId) {
var result = null
await CreateCheckoutSession(
"PRODUCT_ID",
tierId,
"UID"
).then(function(response) {
result = response
})
return result;
};
async function LoadProducts() {
var result = []
await GetProducts("PRODUCT_ID").then(function(response) {
for (var i = 0; i < response.length; i++) {
var tier_id = response[i]["tier_id"]
CheckoutSessionUrl(tier_id).then(function(tier_response) {
response[i]["url"] = tier_response;
})
}
console.log("printing tiers");
console.log(response);
return response;
}).then(function(response){
result = response;
setTiers(result)
return result;
});
return result;
}
useEffect(() => {
// Some initialization logic here
LoadProducts().then(function(response) {
console.log("setting tiers")
console.log(response)
//setTiers(response)
})
}, []);
return (
<div className="bg-white">
<div className="max-w-7xl mx-auto py-24 px-4 sm:px-6 lg:px-8">
<div className="sm:flex sm:flex-col sm:align-center">
<h1 className="text-5xl font-extrabold text-gray-900 sm:text-center">Pricing Plans</h1>
<p className="mt-5 text-xl text-gray-500 sm:text-center">
Start building for free, then add a site plan to go live. Account plans unlock additional features.
</p>
</div>
<div className="mt-12 space-y-4 sm:mt-16 sm:space-y-0 sm:grid sm:grid-cols-2 sm:gap-6 lg:max-w-4xl lg:mx-auto xl:max-w-none xl:mx-0 xl:grid-cols-4">
{tiers.map((tier) => (
<div key={tier.name} className="border border-gray-200 rounded-lg shadow-sm divide-y divide-gray-200">
<div className="p-6">
<h2 className="text-lg leading-6 font-medium text-gray-900">{tier.name}</h2>
<p className="mt-4 text-sm text-gray-500">{tier.description}</p>
<p className="mt-8">
<span className="text-4xl font-extrabold text-gray-900">${tier.price}</span>{' '}
<span className="text-base font-medium text-gray-500">/mo</span>
</p>
<a
href={tier.url}
className="mt-8 block w-full bg-gray-800 border border-gray-800 rounded-md py-2 text-sm font-semibold text-white text-center hover:bg-gray-900"
>
Buy {tier.name}
</a>
</div>
<div className="pt-6 pb-8 px-6">
<h3 className="text-xs font-medium text-gray-900 tracking-wide uppercase">What's included</h3>
<ul role="list" className="mt-6 space-y-4">
{tier.users.map((feature) => (
<li key={feature} className="flex space-x-3">
{/*<CheckIcon className="flex-shrink-0 h-5 w-5 text-green-500" aria-hidden="true" />*/}
<span className="text-sm text-gray-500">{feature}</span>
</li>
))}
</ul>
</div>
</div>
))}
</div>
</div>
</div>
)
}
so if you look at the href={tier.url} it's not being set correctly. I feel like I'm not doing this correctly. Would love some feedback on how to actually get this working properly. IT looks like the tier.url new field isn't being set correctly (doesn't exist in the initial request but all the other attributes work).
The other option I wanted to do was when a user clicked a Button, it would generate a URL and redirect a user to that new external url but the navigation kept breaking.

Oh I found out what was happening. I needed to replace
await GetProducts("PRODUCT_ID").then(function(response) {
with
await GetProducts("PRODUCT_ID").then(async function(response) {

Related

Search query box doesn't recommend places in Vue automatically

If you run my below code, it is supposed to open up a website. This works fine, however, when I go to the input box it is supposed to recommend places with the characters given immediately. This isn't working.
I am doing a tutorial and am currently at 12:41 of this video.
https://www.youtube.com/watch?v=5pTzHoliXUQ&list=PL4cUxeGkcC9hfoy8vFQ5tbXO3vY0xhhUZ&index=5
Here is my code:
<template>
<main class="container text-white" >
<div class="pt-4 mb-8 relative">
<input type="text"
v-model = "searchQuery"
#input="getSearchResults"
placeholder="Search for city or state"
class="py-2 px-1 w-full bg-transparent border-b
focus:border-weather-secondary focus:outline-none focus:shadow-[0px_1px_0_0_#004E71]">
<ul class="absolute bg-weather-secondary text-white w-full shadow-md py-2 px-1 top-[66px]">
<li v-for="searchResult in mapboxSearchResults" :key ="searchResult.id" class="py-2 cursor-pointer">
{{searchResult.place_name}}
</li>
</ul>
</div>
</main>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const mapboxAPIkey ="pk.eyJ1IjoiYWhhbmFhYSIsImEiOiJjbGJqc3d5NG0xNGd5M3BtbWppcmgyNzE4In0.L-HsHWAbshfvVY_xixizNw"
const searchQuery = ref("");
const queryTimeout = ref(null)
const mapboxSearchResults = ref(null)
const getResults = () => {
clearTimeout(queryTimeout.value)
queryTimeout.value = setTimeout(async() => {
if(searchQuery.value !== ""){
const result = await axios.get(
`https://api.mapbox.com/geocoding/v5/mapbox.places/${searchQuery.value}.json?access_token=${mapboxAPIkey}&types=place`
)
mapboxSearchresults.value = result.data.features
return;
}
mapboxSearchresults.value = null
}, 300)
}
</script>
I tried to run the code with npm run dev in the terminal and everything was working fine up until now.

How to apply onClick in the input

As you can this is a string js and I want to use onClick on the input whose view is being toggled by selecting the boolean value edit. Please suggest how to use onchnage here.
Already tried normal HTML onchange (not working)
onchange="${onchnage}"
Pls, suggest if you happen to know the answer.
export const DefaultNode = (d, selectedNodeIds, edit, fomatOptions, inputOnclick) => {
const mainData = d.data.data
return `<div style='background:${selectedNodeIds.length!==0 ? (selectedNodeIds.includes(d.data.id) ? `rgba(${ fomatOptions.nodeBg.r }, ${ fomatOptions.nodeBg.g }, ${ fomatOptions.nodeBg.b }, ${ fomatOptions.nodeBg.a })`: "#fff"): `rgba(${ fomatOptions.nodeBg.r }, ${ fomatOptions.nodeBg.g }, ${ fomatOptions.nodeBg.b }, ${ fomatOptions.nodeBg.a })`};
color:${selectedNodeIds.length!==0 ?(selectedNodeIds.includes(d.data.id) ?`rgba(${ fomatOptions.textColor.r }, ${ fomatOptions.textColor.g }, ${ fomatOptions.textColor.b }, ${ fomatOptions.textColor.a })`:'#000'): `rgba(${ fomatOptions.textColor.r }, ${ fomatOptions.textColor.g }, ${ fomatOptions.textColor.b }, ${ fomatOptions.textColor.a })`}'
class=${`"w-[250px] p-3 rounded-[15px] relative border-[3px] h-[140px] ${selectedNodeIds.includes(d.data.id)? 'drop-shadow-md' :"shadow"} ${ selectedNodeIds.includes(d.data.id) && fomatOptions.fontFamily.value}"`}>
<div class='flex justify-between w-full '>
<div class="">
${edit? `<input onclick='${inputOnclick}' class="fullName text-[13px] font-semibold" value="${mainData.name}"/>` : `<div class=" text-[13px] font-semibold">${mainData.name} </div>`}
<div class=" text-[11px] opacity-70 mt-0.5 font-medium">${mainData.position } </div>
<div class='mt-2'>
<div class=" text-[11px] opacity-70 mt-0.5 font-medium">${mainData.email } </div>
<div class=" text-[11px] opacity-70 mt-0.5 font-medium">${mainData.phone } </div>
</div>
</div>
<img class='w-10 h-10 mr-2 rounded-[10px]' src=${mainData.imgUrl} />
</div>
<div class='flex pt-4 justify-between items-center'>
<p class='text-[10px] font-medium uppercase bg-theme-gray px-2 text-black rounded-full py-0.5'>${mainData.department}</p>
<p class='text-[10px] font-medium uppercase mr-2'>${mainData.location}</p>
</div>
${((selectedNodeIds.includes(d.data.id))) ? `<div class="absolute left-4 -top-5 font-semibold text-[10px] p-1 bg-gray-400 text-white rounded-t-md">
Selected
</div>`: `<p></p>`}
</div>`
}
I believe what you're missing here is the different naming conventions for default HTML event listeners in React, not all of your code is here so I'm assuming you do not have a custom function called onchange, but in React its called onChange (or onClick, etc) so you're looking something like this for your code snippet.
onChange="${onchnage}"
Also double-check to make sure you have all your syntax and spelling correct. Also for writing better JSX for returning HTML elements you can write code like the following
return (
<div>
<p>Text here</p>
</div>
);

Finding the comma, where is it coming from? Javascript Arrays within Objects [duplicate]

This question already has answers here:
Unexpected comma using map()
(4 answers)
Closed last year.
Ok so i have hacked something together it works as intended but I get a comma between entries more than one.
I have this
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
let markup = '';
markup = `
<section>
<div class="inner-element">
<div class="question flex justify-between px-6 py-4 bg-purple-800">
<span class="text-base text-white font-bold">HCP365 Pixels</span>
<button><i class="fas fa-plus-circle"></i></button>
</div>
<div class="answer hideText">
${HCP365Object.sitePixels.map((x) => x)}
${HCP365Object.searchPixels.map((x) => x)}
${HCP365Object.emailPixels.map((x) => x)}
${HCP365Object.programmaticPixels.map((x) => x)}
</div>
</div>
<div class="inner-element">
<div class="question flex justify-between px-6 py-4 bg-gray-500">
<span class="text-base text-white font-bold">NON HCP365 Pixels</span>
<button><i class="fas fa-plus-circle"></i></button>
</div>
<div class="answer hideText">
${HCP365Object.nonHCP365Pixels.map((x) => x)}
</div>
</div>
`;
markup += '</section>';
sendResponse(markup);
});
Which in turn gives me this:
Where the little bugger is here:
Between the element which is :
const pixelInfo = `
<div class="element">
<div class="question flex justify-between px-6 py-4 ${colorGrade}">
<span class="text-base text-white font-bold">${pixelType}</span>
<button><i class="fas fa-plus-circle"></i></button>
</div>
<div class="answer hideText">
<span id="pixel-url" class="c-word-wrap text-sm font-mono">${pixelUrl}</span>
<span id="query-params">${queryParams}</span>
</div>
</div>
`;
Each pixelInfo is pushed into it's own array which is within an object. Is this comma here because I am mapping through an array and it's chucking out the , ? Is there a way to get rid of it?
This is my whole code
const HCP365Object = {
sitePixels: [],
searchPixels: [],
emailPixels: [],
programmaticPixels: [],
nonHCP365Pixels: [],
};
// Function to break down the http request into our pixel url with a subset of associating query params.
const onBeforeSendHeadersListener = function (details) {
let regex = /[?&]([^=#]+)=([^&#]*)/g,
pixelType,
pixelUrl = `${details.url}`,
queryParams,
params = {},
match,
colorGrade;
if (details.url.includes('&ch=1&')) {
pixelType = 'HCP365 Site Pixel';
colorGrade = 'bg-purple-600';
} else if (details.url.includes('&ch=2&')) {
pixelType = 'HCP365 Search Pixel';
colorGrade = 'bg-purple-500';
} else if (details.url.includes('&ch=3&')) {
pixelType = 'HCP365 Email Pixel';
colorGrade = 'bg-purple-400';
} else if (details.url.includes('&ch=4&')) {
pixelType = 'HCP365 Programmatic Pixel';
colorGrade = 'bg-purple-300';
} else {
pixelType = 'Non HCP365 PP Pixel';
colorGrade = 'bg-gray-400';
}
// Splitting url's query params out to key value pairs
while ((match = regex.exec(details.url))) {
params[match[1]] = match[2];
}
// Looping through object's key value pair to place into divs
for (const [key, value] of Object.entries(params)) {
queryParams += `
<div class="text-sm my-1">
<span class="font-bold uppercase mr-1">${key}: </span>
<span class="font-normal font-mono capitalize c-word-wrap">${value}</span>
</div>
`;
}
const pixelInfo = `
<div class="element">
<div class="question flex justify-between px-6 py-4 ${colorGrade}">
<span class="text-base text-white font-bold">${pixelType}</span>
<button><i class="fas fa-plus-circle"></i></button>
</div>
<div class="answer hideText">
<span id="pixel-url" class="c-word-wrap text-sm font-mono">${pixelUrl}</span>
<span id="query-params">${queryParams}</span>
</div>
</div>
`;
// push the relevant pixel into the correct array
if (details.url.includes('&ch=1&')) {
HCP365Object.sitePixels.push(pixelInfo);
} else if (details.url.includes('&ch=2&')) {
HCP365Object.searchPixels.push(pixelInfo);
} else if (details.url.includes('&ch=3&')) {
HCP365Object.emailPixels.push(pixelInfo);
} else if (details.url.includes('&ch=4&')) {
HCP365Object.programmaticPixels.push(pixelInfo);
} else {
HCP365Object.nonHCP365Pixels.push(pixelInfo);
}
return HCP365Object;
};
// Apply the function entered to each header coming from contextweb domain
chrome.webRequest.onBeforeSendHeaders.addListener(
onBeforeSendHeadersListener,
{
urls: ['https://*.contextweb.com/bh/*'],
},
['requestHeaders']
);
// Initiate connection to send Message over to popup.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
let markup = '';
markup = `
<section>
<div class="inner-element">
<div class="question flex justify-between px-6 py-4 bg-purple-800">
<span class="text-base text-white font-bold">HCP365 Pixels</span>
<button><i class="fas fa-plus-circle"></i></button>
</div>
<div class="answer hideText">
${HCP365Object.sitePixels.map((x) => x)}
${HCP365Object.searchPixels.map((x) => x)}
${HCP365Object.emailPixels.map((x) => x)}
${HCP365Object.programmaticPixels.map((x) => x)}
</div>
</div>
<div class="inner-element">
<div class="question flex justify-between px-6 py-4 bg-gray-500">
<span class="text-base text-white font-bold">NON HCP365 Pixels</span>
<button><i class="fas fa-plus-circle"></i></button>
</div>
<div class="answer hideText">
${HCP365Object.nonHCP365Pixels.map((x) => x)}
</div>
</div>
`;
markup += '</section>';
sendResponse(markup);
});
I know it's very hacky! But it works
Taking another look at
markup = `
...
<div class="answer hideText">
${HCP365Object.nonHCP365Pixels.map((x) => x)}
</div>
...
`;
The problem is HCP365Object.nonHCP365Pixels.map((x) => x). map() returns an array, and calling toString() will list all items separated by commas.
You will need to join() these to build the content without the commas.
Also, since .map((x) => x) just returns a copy of the array you can get rid of that too.
markup = `
...
<div class="answer hideText">
${HCP365Object.nonHCP365Pixels.join('')}
</div>
...
`;
You should use .join('') everywhere you use .map((x) => x)

filtering data with vue js

I have loaded a data from API and displayed here with VueJS. I have users information inside users[] array. I also have users with two types of plan: basic_plan and standard_plan. Currently it shows all users.
Now I want to apply filters equally to this example: https://codepen.io/marn/pen/jeyXKL?editors=0010
I also got an error filter not defined
Filters:
<input type="radio" v-model="selectedItems" value="All" /> All
<input type="radio" v-model="selectedItems" value="basic_plan" /> Basic
<ul
v-for="(user, index) in selectedUser.data"
:key="index"
class="watchers divide-y divide-gray-200"
>
<li class="py-4">
<div class="mx-4 flex space-x-3">
<span
class="inline-flex items-center justify-center h-8 w-8 rounded-full bg-gray-500"
>
</span>
<div class="flex-1 space-y-1">
<h3 class="text-sm font-medium">
{{ user.name }}
</h3>
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-indigo-500">
{{ user.plan }}
</p>
</div>
</div>
</div>
</li>
</ul>
</div>
</template>
<script
export default {
data() {
return {
users: [],
selectedItems:"All"
};
},
created() {
this. users();
},
methods: {
users {
axios
.get('api/users')
.then(response => {
this.users = response.data;
}
},
computed: {
selectedUser: function() {
if(this.selectedItems ==="All"){
return this.users
}else{
return this.users.data.filter(function(item) {
console.log(item)
return item.plan === this.selectedItems;
});
}
}
}
};
</script>
when All is selected vue dev tool shows this
selectedUser:Object //OBJECT SHOWING
data:Array[10]
links:Object
meta:Object
but when basic radio is selected vue shows this
selectedUser:Array[1] //ARRAY SHOWING
0:Object
price:"10"
plan:"basic_planl"
If you want to filter out specific users you must apply the "filter" function to the users variable like this:
this.users.filter(...)
With this function you then can filter the users based on their plan like this:
this.users.filter((user) =>
user.plan === this.selectedItems;
});
For a modern approach I used an arrow function. And without using curly brackets the statement inside the function is returned by default, so that's why there is no "return" statement.
Try this way instead, as you are already using v-for in your HTML, you can conveniently filter out users without any more loops, if you are getting value as "basic_plan" in the "user.plan" key.
Also, I think that you should move your v-for to <li> tag instead of <ul> along with the validation on <ul> if there are no users in the array.
<template>
<div>
<input type="radio" v-model="selectedItems" value="All" /> All
<input type="radio" v-model="selectedItems" value="basic_plan" /> Basic
<ul v-if="selectedUser.data.length" class="watchers divide-y divide-gray-200">
<li v-for="(user, index) in selectedUser.data" :key="index" class="py-4">
<div v-if="filterUser(user)" class="mx-4 flex space-x-3">
<span class="inline-flex items-center justify-center h-8 w-8 rounded-full bg-gray-500"></span>
<div class="flex-1 space-y-1">
<h3 class="text-sm font-medium">
{{ user.name }}
</h3>
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-indigo-500">
{{ user.plan }}
</p>
</div>
</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
users: [],
selectedItems:"All"
};
},
methods: {
filterUser(user){
if(this.selectedItems === 'All'){
return true;
}
if(this.selectedItems === 'basic_plan'){
return this.selectedItems === user.plan;
}
}
},
}
</script>

Change button based on click vuejs tailwind

I have expanded this code for my own learning abilities. I understand I can definitely shorten this down a LOT but I am trying to learn and expand my frontend experience.
So I have the code below. When localstorage it set to true/false it picks up the right v-if/else section. Now, what I need to do is set the local storage based on button click.
What is the best way to accomplish this?
<div v-if="privateChat == 'false'">
<button type="button">
<a key="privateChat" href="#" class="bg-red-900 text-gray-100 hover:bg-gray-800 hover:text-white group w-full p-3 rounded-md flex flex-col items-center text-xs font-medium">
<ChatIcon class="h-6 w-6 text-white"/>
<span class="pt-2">Private Chat OFF</span>
</a>
</button>
</div>
<div v-else>
<button type="button">
<a key="privateChat" href="#" class="bg-green-900 text-gray-100 hover:bg-gray-800 hover:text-white group w-full p-3 rounded-md flex flex-col items-center text-xs font-medium">
<ChatIcon class="h-6 w-6 text-white"/>
<span class="pt-2">Private Chat ON</span>
</a>
</button>
</div>
<script>
export default {
data() {
return {
privateChat: (localStorage.getItem("privateChat") === 'true') ? 'true' : 'false',
}
},
methods: {
clickPrivateChat (value) {
this.privateChat = value === true ? "true" : "false";
localStorage.setItem("privateChat", value);
},
setup() {
const enabled = ref(privateChat)
let value = localStorage.getItem("privateChat");
let privateChat = (value === 'true');
}
</script>
There are several improvements you can make...
use actual true/false values instead of "true", "false" strings
DRY: you just need one button; use a Vue computed value to show "ON" or "OFF"
use conditional :class logic to apply bg-green-900 class
script:
data() {
return {
privateChat: (localStorage.getItem("privateChat") === true) ? true : false,
}
},
computed: {
onOrOff() {
return this.privateChat ? 'ON' : 'OFF'
}
},
methods: {
clickPrivateChat (value) {
this.privateChat = !this.privateChat
localStorage.setItem("privateChat", value)
},
setup() {
const enabled = ref(privateChat)
let value = localStorage.getItem("privateChat")
let privateChat = (value === true)
}
},
markup:
<div>
<button type="button" #click="clickPrivateChat">
<a key="privateChat" href="#" :class="privateChat?'bg-green-900':''" class="bg-red-900 text-gray-100 hover:bg-gray-800 hover:text-white group w-full p-3 rounded-md flex flex-col items-center text-xs font-medium">
<span class="pt-2">Private Chat {{ onOrOff }}</span>
</a>
</button>
</div>
improved Vue approach

Categories