On my site there is a language switch, it works all by standards, in the template comes arrays of languages:
$headerBar = [
'langs' => [
[
'name' => 'Українська',
'img' => 'images/flags/ua.png',
'code' => 'ua',
'active' => true,
],
[
'name' => 'Русский',
'img' => 'images/flags/ru.png',
'code' => 'ru',
'active' => false,
],
[
'name' => 'English',
'img' => 'images/flags/uk.png',
'code' => 'uk',
'active' => false,
]
]
];
Language output template:
<template slot="language-bar">
<language-switcher v-bind:langs='{!! json_encode($langs) !!}'></language-switcher>
</template>
And the component vue.js itself:
<template>
<div class="lang-currency-switcher-wrap" :class="{ show: isVisible }">
<div class="lang-currency-switcher dropdown-toggle" #click="isVisible = true">
<span class="language">
<img :alt="activeLang.name" :src="activeLang.img"></span>
<span class="currency">{{ activeLang.code }}</span>
</div>
<div class="dropdown-menu">
<a v-for="lang in langs" v-if="!lang.active" class="dropdown-item">
<img :src="lang.img" alt="">{{ lang.name }}
</a>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['langs'],
data() {
return {
isVisible: false
};
},
computed: {
activeLang() {
let activeLang = [];
for (let lang of this.langs) {
if (lang.active) {
activeLang = lang;
}
}
return activeLang;
}
},
mounted() {
}
}
</script>
It works like this: The server renders an array of languages with clever calculations, one of which is active, it all comes to the blade template and the vue.js component is already called
The component vue.js in its turn outputs an available list of languages except active, and the active calculates and displays for the user.
When the user clicks on the language wrapper, then vue displays the entire available list.
Now the question is:
How correctly to hide all languages, in that cases, if the user clicked on any of the areas of the site, except the most wrapper languages?
Also interesting is the criticism, perhaps it could have been done differently, in a more beautiful way :)
Thank you.
Related
Astro.js & Javascript
Inside of a component i'm trying to map over some data one time and display different data from each rendered component in another file.
The component responsible for mapping the data
---
const skills = [
{
name: 'Frontend',
id: 1,
frontend: [ 'HTML', 'CSS' ]
},
{
name: 'Backend',
id: 2,
backend: [ 'Firebase', 'PHP' ]
}
]
---
{skills.map(skill => (
<li>
<h4 class="role me-2 astro-FP6HE37A"> {skill}
<span class="invert position-absolute d-flex align-items-center justify-content-center h-100 w-100 astro-FP6HE37A"> {skill}</span>
</h4>
</li>
))}
The file responsible for displaying the said component
---
import SkillsIcon from '../components/SkillsIcon.astro';
---
<h3>Frontend</h3>
<SkillsIcon />
<h3>Backend</h3>
<SkillsIcon />
you can place everything in one component like this, note renaming generic field as 'tags' not to create a dependency of the array elements to their own content
---
const skills = [
{
name: 'Frontend',
id: 1,
tags: [ 'HTML', 'CSS' ]
},
{
name: 'Backend',
id: 2,
tags: [ 'Firebase', 'PHP' ]
}
]
---
{skills.map(skill => (
<h3>{skill.name}</h3>
<ul>
{skill.tags.map(tag => (
<li>
<h4 class="role ... ">
<span class="invert ...">{tag}</span>
</h4>
</li>
))
}
</ul>
))}
If it is important to have two separate components, you can place your data on an external JSON file and import it in both components and pass as argument to the child component the array index you want to map in the component, like this
{skills.map((skill,index) => (
<h3>{skill.name}</h3>
<SkillIcon index={index} />
))}
reference for json import in astro https://docs.astro.build/en/guides/imports/#json
I'm having issue where when i put my state into index of an array, it gives error.
Here is there line of bug : {WorkData[state].title}
What i need to do is to display the elements of WorkData which is a array with objects : title, content, img math etc...
const WorkData = [
{
id: 0,
title: 'title',
subtext: 'React/Express',
content: 'content',
imgPath: 'imgPath',
},
{
id: 1,
title: 'Little Hero Academy',
subtext: 'React/API REST',
content:
'content,
imgPath: 'imgPath',
},
{
id: 2,
title: 'title',
subtext: 'subtext',
content: 'content',
imgPath: 'imgPath',
},
First, i have a list of cards with a button that contains data:
<div className="work-container flex">
<div className="work-bloc zoom">
<div className="work-hover" />
<div className="work-text">
<div className="text-title">{WorkData[0].title}</div>
<span className="subtext">{WorkData[0].subtext}</span>
</div>
<div
onClick={togglePopup}
data-id={WorkData[0].id}
className="work-showmore button2"
>
Show More
</div>
</div>
</div>
I store the data in state with this function :
const [id, setId] = useState();
const togglePopup = (e) => {
setIsOpen(!isOpen);
setId(e.target.dataset.id);
};
I need that data to get to the popup,
<Popup
togglePopup={togglePopup}
closePopup={closePopup}
isOpen={isOpen}
id={id}
/>
I pass the state to my popup component and try to display the value of id which is displayed
Also,i need to make the popup display of the content of the array that belong to the id (index) i passed in state : workData 1 2 3 etc...
const Popup = ({ closePopup, isOpen, id }) => {
return (
<div className={`popup-container ${isOpen ? 'opened' : 'closed'}`}>
<div className={`popup ${isOpen ? 'opened2' : 'closed2'}`}>
<span className='popup-title'>
{WorkData[id].title}
{id}
</span>
<div className='image-container'>
<img
src='https://i.picsum.photos/id/1060/536/354.jpg?blur=2&hmac=0zJLs1ar00sBbW5Ahd_4zA6pgZqCVavwuHToO6VtcYY'
alt=''
/>
</div>
<p>
Team projet with React and API REST. Our goal was to make an app with
a choosen API and make something out of it. We did a superhero-themed
website with games for children.
<br />
Check on <AiFillGithub className='menuicon' />
</p>
<div onClick={closePopup} className='close-icon button2'>
Show less{' '}
</div>
</div>
</div>
);
};
but i get this error :
**TypeError: _WorkData__WEBPACK_IMPORTED_MODULE_1__.default[id] is undefined**
Thanks in advance for any suggestion
Ok i fixed, it i changed the way of doing it, i mapped my workcard components so i can easily retrieve the id, title, etc, and i didnt change the event onclick to catch the card.id in state, i passed the state to parent, then to the popup, and then into the popup component i just imported the WorkData.js and simply did : WorkData[state-with-id].title etc
See sample of code below
works.js
const togglePopup = (e) => {
setIsOpen(!isOpen);
setPopupId(e.target.dataset.id);
};
const closePopup = () => {
setIsOpen(false);
};
useEffect(() => {
setCards(workData);
}, []);
workcards.js
<div
onClick={togglePopup}
className='work-showmore button2'
data-id={workcard.id}
>
popup.js
import workData from './workData';
<span className='popup-title'>{workData[popupId].title}</span>
I'm working on a RealWord App for Vue project. I'm trying to find a way to hide the phrase "test", so that it cannot show any article with the "test" phrase.
TagList.vue Component:
<template>
<ul class="tag-list">
<li
class="tag-default tag-pill tag-outline"
v-for="(tag, index) of tags"
:key="index"
>
<span v-text="tag" />
</li>
</ul>
</template>
<script>
export default {
name: "TagList",
props: {
tags: Array
}
};
</script>
To hide tags that contain "test", use a computed prop to get a filtered array of tags[]:
export default {
computed: {
filteredTags() {
return this.tags.filter(tag => tag !== 'test')
}
}
}
Then, update your v-for to use this computed prop:
<li v-for="(tag, index) of filteredTags">
i'm building this application with the help of the RestCountries Api to be able to show each country with basic details on a grid, and after a click on each box the app will show a modal with more detailed informations. That's my code so far:
class App extends React.Component{
constructor (props){
super (props);
this.state={
countries : [],
clickedCountry: {},
modalOn : false,
}
}
componentDidMount(){
axios.get(`https://restcountries.eu/rest/v2/all`)
.then(res => {
const data = res.data;
this.setState({
countries : data
})
let countries = this.state.countries
console.log(countries);
})
}
showInfo = (name) => {
this.setState({
clickedCountry : this.state.countries.find(it => it.name===name),
modalOn : true
});
}
closeModal =()=>{
this.setState({
modalOn : false
})
}
render() {
return (
<div className="container">
{this.state.countries.map(country=>
<Country name={country.name}
key={country.name}
population ={country.population}
region={country.region}
capital={country.capital}
flag={country.flag}
showInfo={this.showInfo}
languages={country.languages}
/>
)}
<div style={{display: this.state.modalOn? "block" : "none"}}>
<Modal closeModal={this.closeModal}
name={this.state.clickedCountry.name}
population={this.state.clickedCountry.population}
region={this.state.clickedCountry.region}
capital ={this.state.clickedCountry.capital}
flag={this.state.clickedCountry.flag}
nativeName ={this.state.clickedCountry.nativeName}
subregion={this.state.clickedCountry.subregion}
topLevelDomain={this.state.clickedCountry.topLevelDomain}
languages={this.state.clickedCountry.languages}
/>
</div>
</div>
)
}
}
Modal component :
const Modal = ({closeModal, name, population, region, capital, flag, languages, nativeName, subregion, topLevelDomain, currencies}) => {
return (
<div className="modal">
<div className="modal-content">
<span onClick={closeModal}>x</span>
<div className="img">
<img src={flag}/>
</div>
<p>{name}</p>
<p>Native name: {nativeName}</p>
<p>population: {population}</p>
<p>Region: {region}</p>
<p>Sub Region: {subregion}</p>
<p>Top level domain: {topLevelDomain}</p>
<p>Capital: {capital}</p>
</div>
</div>
)
}
So far for now i have mapped each country and the modal on click is showing more detailed informations. The problem now is the fact i need to access in the api an array that is nested inside an object:
area: 91
gini: null
timezones: ["UTC-04:00"]
borders: []
nativeName: "Anguilla"
numericCode: "660"
currencies: [{…}]
languages: [{…}]
translations: {de: "Anguilla", es: "Anguilla", fr: "Anguilla", ja: "アンギラ", it: "Anguilla", …}
flag: "https://restcountri
I need to access the languages array. Now if i try to map languages inside the country component, i can display the informations. But i want to show the langauges only on the modal component, and if i map the clickedCountry state responsible for the modal i will receive the error that "languages" is undefined. How it comes if is the same object filtered through the find function? Hope i was clear guys, cheers.
I know you understood whats happened!, just add this to Modal component
<ul>
{
languages && languages.map(lan=> {return <li>{lan.name}</li>} )
}
</ul>
I've created a simple component named DefaultButton.
It bases on properties, that are being set up whenever this component is being created.
The point is that after mounting it, It does not react on changes connected with "defaultbutton", that is an object located in properties
<template>
<button :class="buttonClass" v-if="isActive" #click="$emit('buttonAction', defaultbutton.id)" >
{{ this.defaultbutton.text }}
</button>
<button :class="buttonClass" v-else disabled="disabled">
{{ this.defaultbutton.text }}
</button>
</template>
<script>
export default {
name: "defaultbutton",
props: {
defaultbutton: Object
},
computed: {
buttonClass() {
return `b41ngt ${this.defaultbutton.state}`;
},
isActive() {
return (this.defaultbutton.state === "BUTTON_ACTIVE" || this.defaultbutton.state === "BUTTON_ACTIVE_NOT_CHOSEN");
}
}
};
</script>
Having following component as a parent one:
<template>
<div v-if="state_items.length == 2" class="ui placeholder segment">
{{ this.state_items[0].state }}
{{ this.state_items[1].state }}
{{ this.current_active_state }}
<div class="ui two column very relaxed stackable grid">
<div class="column">
<default-button :defaultbutton="state_items[0]" #buttonAction="changecurrentstate(0)"/>
</div>
<div class="middle aligned column">
<default-button :defaultbutton="state_items[1]" #buttonAction="changecurrentstate(1)"/>
</div>
</div>
<div class="ui vertical divider">
Or
</div>
</div>
</template>
<script type="text/javascript">
import DefaultButton from '../Button/DefaultButton'
export default {
name: 'changestatebox',
data() {
return {
current_active_state: 1
}
},
props: {
state_items: []
},
components: {
DefaultButton
},
methods: {
changecurrentstate: function(index) {
if(this.current_active_state != index) {
this.state_items[this.current_active_state].state = 'BUTTON_ACTIVE_NOT_CHOSEN';
this.state_items[index].state = 'BUTTON_ACTIVE';
this.current_active_state = index;
}
},
},
mounted: function () {
this.state_items[0].state = 'BUTTON_ACTIVE';
this.state_items[1].state = 'BUTTON_ACTIVE_NOT_CHOSEN';
}
}
</script>
It clearly shows, using:
{{ this.state_items[0].state }}
{{ this.state_items[1].state }}
{{ this.current_active_state }}
that the state of these items are being changed, but I am unable to see any results on the generated "DefaultButtons". Classes of objects included in these components are not being changed.
#edit
I've completely changed the way of delivering the data.
Due to this change, I've abandoned the usage of an array; instead I've used two completely not related object.
The result is the same - class of the child component's object is not being
DefaulButton.vue:
<template>
<button :class="buttonClass" v-if="isActive" #click="$emit('buttonAction', defaultbutton.id)" >
{{ this.defaultbutton.text }}
</button>
<button :class="buttonClass" v-else disabled="disabled">
{{ this.defaultbutton.text }}
</button>
</template>
<style lang="scss">
import './DefaultButton.css';
</style>
<script>
export default {
name: "defaultbutton",
props: {
defaultbutton: {
type: Object,
default: () => ({
id: '',
text: '',
state: '',
})
}
},
computed: {
buttonClass() {
return `b41ngt ${this.defaultbutton.state}`;
},
isActive() {
return (this.defaultbutton.state === "BUTTON_ACTIVE" ||
this.defaultbutton.state === "BUTTON_ACTIVE_NOT_CHOSEN");
}
}
};
</script>
ChangeStateBox.vue:
<template>
<div class="ui placeholder segment">
{{ this.state_first.state }}
{{ this.state_second.state }}
{{ this.current_active_state }}
<div class="ui two column very relaxed stackable grid">
<div class="column">
<default-button :defaultbutton="state_first" #buttonAction="changecurrentstate(0)"/>
</div>
<div class="middle aligned column">
<default-button :defaultbutton="state_second" #buttonAction="changecurrentstate(1)"/>
</div>
</div>
<div class="ui vertical divider">
Or
</div>
</div>
</template>
<script type="text/javascript">
import DefaultButton from '../Button/DefaultButton'
export default {
name: 'changestatebox',
data() {
return {
current_active_state: 1
}
},
props: {
state_first: {
type: Object,
default: () => ({
id: '',
text: ''
})
},
state_second: {
type: Object,
default: () => ({
id: '',
text: ''
})
},
},
components: {
DefaultButton
},
methods: {
changecurrentstate: function(index) {
if(this.current_active_state != index) {
if(this.current_active_state == 1){
this.$set(this.state_first, 'state', "BUTTON_ACTIVE_NOT_CHOSEN");
this.$set(this.state_second, 'state', "BUTTON_ACTIVE");
} else {
this.$set(this.state_first, 'state', "BUTTON_ACTIVE");
this.$set(this.state_second, 'state', "BUTTON_ACTIVE_NOT_CHOSEN");
}
this.current_active_state = index;
}
},
},
created: function () {
this.state_first.state = 'BUTTON_ACTIVE';
this.state_second.state = 'BUTTON_ACTIVE_NOT_CHOSEN';
}
}
</script>
You're declaring props wrong. It is either an array of prop names or it is an object with one entry for each prop declaring its type, or it is an object with one entry for each prop declaring multiple properties.
You have
props: {
state_items: []
},
but to supply a default it should be
props: {
state_items: {
type: Array,
default: []
}
},
But your problem is most likely that you're mutating state_items in such a way that Vue can't react to the change
Your main problem is the way you are changing the button state, according with Array change detection vue can't detect mutations by indexing.
Due to limitations in JavaScript, Vue cannot detect the following
changes to an array:
When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue When you modify the length of the
array, e.g. vm.items.length = newLength
In case someone will be having the same issue:
#Roy J as well as #DobleL were right.
The reason behind this issue was related with the wrong initialization of state objects.
According to the documentation:
Vue cannot detect property addition or deletion.
Since Vue performs the getter/setter conversion process during instance
initialization, a property must be present in the
data object in order for Vue to convert it and make it reactive.
Before reading this sentence, I used to start with following objects as an initial data:
var local_state_first = {
id: '1',
text: 'Realized',
};
var local_state_second = {
id: '2',
text: 'Active'
};
and the correct version of it looks like this:
var local_state_first = {
id: '1',
text: 'Realized',
state: 'BUTTON_ACTIVE'
};
var local_state_second = {
id: '2',
text: 'Active',
state: 'BUTTON_ACTIVE'
};
whereas declaring the main component as:
<change-state-box :state_first="local_state_first" :state_second="local_state_second" #buttonAction="onbuttonAction"/>
Rest of the code remains the same ( take a look at #edit mark in my main post )