I have a form that once certain checkboxes are checked, I need them to v-show additional form components. I can get it to work, sort of. If you only choose 1 option, it will populate the correct form, however, If multiple choices are made it doesn't show the additional forms components. Here is my code. It seems that am getting the correct values through the store, I'm just missing something. I apologize in advance if this is a duplicate question. I feel like I've read all of stackoverflow in the last few weeks :) Here is a screencast of it somewhat working http://somup.com/c3hD0TtnJh
I'm using Formkit, Vue3, Pinia. Thanks in advance.
App.vue
<template>
<ReasonForVisit />
<Sports v-show="data.reasonForVisit == 'si' " />
<WorkComp v-show="data.reasonForVisit == 'wc' " />
<Accident v-show="data.reasonForVisit == 'aa' " />
</template>
<script>
import ReasonForVisit from './components/ReasonForVisit.vue'
import Sports from './components/Sports.vue'
import WorkComp from './components/WorkComp.vue'
import Accident from './components/Accident.vue'
import { useFormStore} from './stores/formStore.js'
const data = useFormStore()
</script>
ReasonForVisit.vue
<template>
<FormKit
v-model="data.reasonForVisit"
type="checkbox"
label="Reason for Visit"
:options="{
we: 'Wellness Check',
aa: 'Auto Accident',
si: 'Sports Injury',
wc: 'Work Comp' }"
validation="required"
#change="data.checkedReason"
/>
<p>reason: {{ data.reasonForVisit }}</p>
</template>
FormStore.js
import { defineStore } from 'pinia'
import { differenceInYears, parseISO } from 'date-fns'
export const useFormStore = defineStore('formStore', {
state: () => ({
reasonForVisit: [],
}),
},
}
)
Your reasonForVisit is an array as you initialize it in FormStore.js as reasonForVisit: [].
Instead of data.reasonForVisit == 'si' and others, if you check for data.reasonForVisit.includes('si'), it should work as intended.
It shouldn't be working for 1 option too right now, I think the only reason it works for the 1 option case is the == operator considers si to be the same as ['si']. You should use strict equality to avoid such bugs in js.
You cannot compare Array with string. You need to iterate it or filter or use include method on it and check are it contains correct word. To do that, read about computed() properties. And if you are new to programming, use === not just == or you end up with bad errors in code.
Example:
<template>
<ReasonForVisit />
<Sports v-show="containsSi" />
<WorkComp v-show="containsWc" />
<Accident v-show="containsAa" />
</template>
<script>
import ReasonForVisit from './components/ReasonForVisit.vue'
import Sports from './components/Sports.vue'
import WorkComp from './components/WorkComp.vue'
import Accident from './components/Accident.vue'
import { useFormStore} from './stores/formStore.js'
const data = useFormStore()
const containsSi = computed(() => data.reasonForVisit.includes('si'))
const containsWc = computed(() => data.reasonForVisit.includes('wc'))
const containsAa = computed(() => data.reasonForVisit.includes('aa'))
</script>
Related
I want a button to toggle between two views on click. The code is:
<template>
<button #click="switchSheet">{{ currentSheet }}</button>
<component :is="sheetView" :key="componentKey" />
</template>
<script setup>
import DisplaySheetClassrooms from "./components/ds-classrooms.vue";
import DisplaySheetChromebooks from "./components/ds-chromebooks.vue";
let sheetView = DisplaySheetClassrooms;
let currentSheet = ref("HS Classrooms");
const switchSheet = () => {
if (sheetView == DisplaySheetClassrooms) {
sheetView = DisplaySheetChromebooks;
currentSheet.value = "HS Chromebooks";
} else {
sheetView = DisplaySheetClassrooms;
currentSheet.value = "HS Classrooms";
}
};
Documentation states
":is can contain either:
the name string of a registered component, OR
the actual imported component object"
I am using the actual imported component but looking at Devtools switchView never changes value on click. I also tried it with string quotes and still no good. I tried solution here but it did not work for me. Could somebody clarify how to get this working . Thanks in advance.
This seems to work for me:
Use a components object and have the component name key (sheetView in your case) used to target the key in the component :is value
example:
<template>
<button #click="switchSheet">{{ currentSheet }}</button>
<component :is="components[currentSheet]" :key="currentSheet" />
</template>
<script setup>
import DisplaySheetClassrooms from "./components/ds-classrooms.vue";
import DisplaySheetChromebooks from "./components/ds-chromebooks.vue";
let currentSheet = ref("DisplaySheetChromebooks");
// create an object key (doesn't need to be a ref)
const components = {
DisplaySheetChromebooks,
DisplaySheetClassrooms,
}
const switchSheet = () => {
if (currentSheet === "DisplaySheetClassrooms) {
currentSheet = "DisplaySheetChromebooks";
} else {
currentSheet = "DisplaySheetClassrooms";
}
};
</script>
I am developing a website with Next JS. This is the first time I use this language and I still have a lot to learn.
Now, I have a Pro subscription to Font Awesome and I am looking to use their component directly in my project, as described in their guides.
https://fontawesome.com/v6.0/docs/web/use-with/react/
https://fontawesome.com/v6.0/docs/web/use-with/react/add-icons
Basically, just using a component like this:
<FontAwesomeIcon icon={brands("google")} />
It works.
The problem, which I can't solve, is how to set the "google" value dynamically, in a previously initialized variable. I need that because this values are coming dynamically from a database.
If I try:
var example = "google";
<FontAwesomeIcon icon={brands(example)} />
Then I receive this error: "Only string literals are supported when referencing icons (use a string here instead)".
Anyone have any ideas?
The way you import your icons uses a babel macro. It means that the referenced icon must be known at build time. It just won't work, because Babel can't figure out what icon should be imported, it can only be done during runtime. That's why it tells you that only string literals are supported.
So you need to use the second method, explained in the docs. You have to have some kind of mapping between icon and the value from DB and then grab the relevant icon. Something like this:
// not sure about exact import
import { faGoogle } from '#fortawesome/free-brand-svg-icons';
// you'd put all valid values that can come from the backend here
const myIcons = {
google: faGoogle
}
// ...
const example = 'google';
<FontAwesomeIcon icon={myIcons[example]} />
The other option is to use library.add, explained here and then do the same as above but instead of imported icon use a string, e.g. google: 'fa-brands fa-google' (again, not sure about exact value here).
Would the following work?
var example = ['fab', 'google'];
<FontAwesomeIcon icon={example} />
P.S. It is recommended to use the let or const keywords with ES6.
// Utils
const UpperFirst = (sentence) => {
if (!sentence || (typeof sentence !== 'string')) return null
return `${sentence.charAt(0).toUpperCase()}${sentence.slice(1)}`
}
// React Generic Component
import React from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import * as solid from '#fortawesome/free-solid-svg-icons'
import * as regular from '#fortawesome/free-regular-svg-icons'
import { UpperFirst } from '#utils'
const GenFontAwe = ({ isReg, customClass, nameIco }) => {
if (!nameIco) return null
const finalName = nameIco.split('-').map((cv, ind) => ind === 0 ? cv : UpperFirst(cv)).join('')
const finalIcon = isReg ? regular[finalName] : solid[finalName]
if(!finalIcon) return null
return <FontAwesomeIcon icon={finalIcon} className={customClass || ''} />
}
export default GenFontAwe
// Consume that way:
<GenFontAwe nameIco='fa-folder-open' isReg customClass="h1 p-5" />
I use the react-tooltip library in my Next.js app.
I noticed that every time I refresh a website while visiting a page that uses the tooltip I get an error:
react-dom.development.js:88 Warning: Prop `dangerouslySetInnerHTML` did not match.
CSS classes are different on the client and on the server
The weird part is I do not get that error while navigating from a random page to a page that uses the react-tooltip.
The tooltip related code:
<StyledPopularityTooltipIcon src="/icons/tooltip.svg" alt="question mark" data-tip="hello world" />
<ReactTooltip
effect="solid"
className="tooltip"
backgroundColor="#F0F0F0"
arrowColor="#F0F0F0"
clickable={true}
/>
I had the same issue, I had to use state to detect when component has been mounted, and show the tooltip only after that.
P.S. You don't see the error when navigating, because the page is not rendered on server when you navigate, it's all front-end :)
In case you are using any server-side rendering (like Next.js) - you will need to make sure your component is mounted first before showing the react-tooltip.
I fixed this by using the following:
import React, { useEffect, useState } from 'react';
const [isMounted,setIsMounted] = useState(false); // Need this for the react-tooltip
useEffect(() => {
setIsMounted(true);
},[]);
return (<div>
{isMounted && <ReactTooltip id={"mytip"} effect={"solid"} />}
<span data-tip={"Tip Here"} data-for={"mytip"}>Hover me</span>
</div>)
You should wrap your JSX in the following component:
import React, { useEffect, useState } from 'react';
const NoSsr = ({ children }): JSX.Element => {
const [isMounted, setMount] = useState(false);
useEffect(() => {
setMount(true);
}, []);
return <>{isMounted ? children : null}</>;
};
export default NoSsr;
Like this:
<NoSsr>
<YourJSX />
</NoSsr>
If you are working with NEXTJS this might be a good approach, you can check the documentation here as well, also if you are working with data-event, globalEventOff or any other prop and is not hiding or not working in your localhost, this only occurs in Development Strict Mode. ReactTooltip works fine in Production code with React 18. So you can set reactStrictMode : false, in your next.config.js to test it locally and then set it back to true, hope this helps :) info reference here
import dynamic from 'next/dynamic'
const ReactTooltip = dynamic(() => import('react-tooltip'), { ssr : false });
function Home() {
return (
<div>
<Button
data-tip
data-event="click focus"
data-for="toolTip"
onClick={():void => ()}
/>
<ReactTooltip id="toolTip" globalEventOff="click"/>
</div>
)
}
export default Home
Have been googling forever and found a way to change the <title>. That way is this: https://github.com/zeit/next.js/tree/master/examples/layout-component
The main problem with this is that everytime someone refresh the site/change page the title goes from http://localhost:3000 to the actual Title (eg. About us) and I'm a bit afraid of how this is affecting the SEO.
What is the correct way of chaning the page title dynamically?
My layout file:
import Link from 'next/link'
import Head from './../node_modules/next/head'
export default function Layout({ children, title = 'Welcome to my website' }) {
return (
<div>
<Head>
<title>{title}</title>
</Head>
{children}
</div>
)
}
Check out next-seo and install it in your next.js application.
yarn add next-seo
# or
npm install --save next-seo
And it will handle the page title and the meta description for you magically.
import React from 'react';
import { NextSeo } from 'next-seo'; // then add the `NextSeo` at any `pages/` that you wish
export default () => (
<>
<NextSeo
title="About Us, or just any title that you wish"
description="Then with a short description here."
/>
<p>Simple Usage</p>
</>
);
I have implemented the same tactic on my own web app here.
Well for me this works,
Import <Head> from next,
import Head from 'next/head'
And in return statement,
<>
<Head>
<title>Page Title</title>
</Head>
<section>
<Your_JSX_CODE>
</section>
</>
If you need a dynamic title/description, for example for the route parameters case, you can do this. (Consider that the page name is [id].js)
import React from 'react'
import { NextSeo } from 'next-seo' //as proposed by #xun
// import Head from "next/head" //alternative solution
const Detail = ({id}) => {
const title = `My ${id} title.`
const description = `My ${id} description.`
return (
<>
<NextSeo
title={title}
description={description}
/>
<p>It works!</p>
</>
)}
export default Detail
And at the end of your file:
export async function getServerSideProps({query}) {
const { id } = query
return {
props: {
id
},
};
}
Good luck!
I reninstalled "next" and "next-routes" in my dependencies and now this works.
I have a reactJS application that I want to make available to multiple clients. Each clients has unique color schemes. I need to be able to import the .css file that corresponds to the specific client.
For example, if client 1 logs into the application, I want to import client1.css. if client 2 logs into the application, I want to import client2.css. I will know the client number once I have validated the login information.
The application contains multiple .js files. Every .js file contains the following at the top of the file
import React from 'react';
import { Redirect } from 'react-router-dom';
import {mqRequest} from '../functions/commonFunctions.js';
import '../styles/app.css';
Is there a way to import .css files dynamically for this scenario as opposed to specifying the .css file in the above import statement?
Thank you
Easy - i've delt with similar before.
componentWillMount() {
if(this.props.css1 === true) {
require('style1.css');
} else {
require('style2.css');
}
}
Consider using a cssInJs solution. Popular libraries are: emotion and styled-components but there are others as well.
I generally recommend a cssInJs solution, but for what you are trying to do it is especially useful.
In Emotion for example they have a tool specifically build for this purpose - the contextTheme.
What cssInJs basically means is that instead of using different static css files, use all the power of Javascript, to generate the needed css rules from your javascript code.
A bit late to the party, I want to expand on #Harmenx answer.
require works in development environments only, once it goes to production you're likely to get errors or not see the css file at all. Here are some options if you, or others, encounter this:
Option 1: Using css modules, assign a variable of styles with the response from the import based on the condition.
let styles;
if(this.props.css1 === true) {
//require('style1.css');
import("./style1.module.css").then((res) => { styles = res;});
} else {
//require('style2.css');
import("./style2.module.css").then((res) => { styles = res;});
}
...
<div className={styles.divClass}>...</div>
...
Option 2: using Suspend and lazy load from react
// STEP 1: create components for each stylesheet
// styles1.js
import React from "react";
import "./styles1.css";
export const Style1Variables = (React.FC = () => <></>);
export default Style1Variables ;
// styles2.js
import React from "react";
import "./styles2.css";
export const Style2Variables = (React.FC = () => <></>);
export default Style2Variables ;
// STEP 2: setup your conditional rendering component
import React, {lazy, Suspense} from "react";
const Styles1= lazy(() => import("./styles1"));
const Styles2= lazy(() => import("./styles2"));
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
{isClient1() ? <Styles1 /> : <Styles2/>}
</Suspense>
{children}
</>
);
};
// STEP 3: Wrap your app
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
Option 3: Use React Helm which will include a link to the stylesheet in the header based on a conditional
class App extends Component {
render() {
<>
<Helmet>
<link
type="text/css"
rel="stylesheet"
href={isClient1() ? "./styles1.css" : "./styles2.css"}
/>
</Helmet>
...
</>
}
}
Personally, I like option 2 because you can set a variable whichClientIsThis() then modify the code to:
import React, {lazy, Suspense} from "react";
let clientID = whichClientIsThis();
const Styles= lazy(() => import("./`${clientID}`.css")); // change your variable filenames to match the client id.
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
<Styles />
</Suspense>
{children}
</>
);
};
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
This way you don't need any conditionals. I'd still recommend lazy loading and suspending so the app has time to get the id and make the "decision" on which stylesheet to bring in.