Depending on the selected language, I need to destruct the object and get the desired value.
How do I do this so as not to destruct the entire object?
const translate = {
"navMenu1": {
"en": "Menu 1",
"ru": "Меню 1"
},
"navMenu2": {
"en": "Menu 2",
"ru": "Меню 2"
},
"navMenu3": {
"en": "Menu 3",
"ru": "Меню 3"
}
}
const Header = props => {
const { lang } = props;
const {
navMenu1,
navMenu2,
navMenu3
} = translate;
return (
<header className={cnGreetingHeader}>
<div>-Logo-</div>
<nav className={cnNav}>
<div className={cnItem}>{navMenu1[lang]}</div>
<div className={cnItem}>{navMenu2[lang]}</div>
<div className={cnItem}>{navMenu3[lang]}</div>
</nav>
</header>
);
};
I want instead
<div className={cnItem}>{navMenu1[lang]}</div>
Use
<div className={cnItem}>{navMenu1}</div>
Important: I would like an answer that uses destructuring assignment, if possible.
Altougth you asked destructing, you can just map the object values, its more scalable, what happens when you have much more nav items?
<nav className={cnNav}>
{Object.values((navMenu) => (
<div key={navMenu.id} className={cnItem}>
{navMenu[lang]}
</div>
))}
</nav>
You could take a nested destructuring and take the outer name as variable.
const
translate = { navMenu1: { en: "Menu 1", ru: "Меню 1" }, navMenu2: { en: "Menu 2", ru: "Меню 2" }, navMenu3: { en: "Menu 3", ru: "Меню 3" } },
Header = props => {
const
{ lang } = props,
{
navMenu1: { [lang]: navMenu1 },
navMenu2: { [lang]: navMenu2 },
navMenu3: { [lang]: navMenu3 }
} = translate;
console.log(navMenu1);
console.log(navMenu2);
console.log(navMenu3);
};
Header({ lang: 'en' });
Related
I need some help. I am using a multilingual static site w/JQuery and JSON but I would like to use it w/simple JS. Most part of the code is finished but I cannot resolve the commented part of the JS successfully (w/JQuery it is working well).
var language, translate, jsData;
// Here is the questioned part in JQuery
translate = function(jsdata) {
$('[block]').each(function(index) {
var strTr;
strTr = jsdata[$(this).attr('block')][$(this).attr('txt')];
$(this).html(strTr);
});
};
document.querySelector("a#hu").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "teszt 1",
"body": "Szia Világ!"
},
"2.md": {
"title": "teszt 2",
"body": "Szia Világ megint!"
}
}
translate()
});
document.querySelector("a#en").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "test 1",
"body": "Hello Word!"
},
"2.md": {
"title": "test 2",
"body": "Hello Word again!"
}
}
translate()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a hrf="#" id="hu">HU</a>
<a hrf="#" id="en">EN</a>
<p block="1.md" txt="title"></p>
<p block="1.md" txt="body"></p>
Please read the comments.
[...document.querySelectorAll("[block]")]
.forEach(block => block.innerHTML = jsData[block.getAttribute("block")][block.getAttribute("txt")]);
let language, jsData;
// Here is the questioned part in JQuery
const translate = () => {
[...document.querySelectorAll("[data-block]")].forEach(
block =>
block.innerHTML = jsData[block.getAttribute("data-block")][block.getAttribute("data-txt")]
);
};
document.querySelector("a#hu").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "teszt 1",
"body": "Szia Világ!"
},
"2.md": {
"title": "teszt 2",
"body": "Szia Világ megint!"
}
}
translate()
});
document.querySelector("a#en").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "test 1",
"body": "Hello Word!"
},
"2.md": {
"title": "test 2",
"body": "Hello Word again!"
}
}
translate()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a hrf="#" id="hu">HU</a>
<a hrf="#" id="en">EN</a>
<p data-block="1.md" data-txt="title"></p>
<p data-block="1.md" data-txt="body"></p>
<p data-block="2.md" data-txt="title"></p>
<p data-block="2.md" data-txt="body"></p>
I have an object like this and I need to filter the rules within each group item, however I need to also return the group name next to the filtered rule
{
"id": "rulesCompany",
"group": [
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [
{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
},
]
}
For example, I need to return the group "Cadastral/Receita Federal" if I search for "Rece" in search field, but I don't know how to filter data inside data.
What I've done so far:
Module.vue
<template>
<div>
<slide-out :visible.sync="isVisible" :title="text.header">
<div slot="header">
<div>
<button class="neo-bt-icon--big slideout__close--button" #click="isVisible=false">
<svg><use xlink:href="/red/neo-assets/images/simple-line-icons.svg#arrow-2-left"></use></svg>
</button>
<h1 class="slideout__header--text">
{{ text.header }}
</h1>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input type="text" class="neo-form-field" placeholder="Buscar" v-model="searchQuery">
<input class="neo-form-toggle__field" :id="selectAllRules" #click="selectAllRules($event)" type="checkbox"/>
<label class="neo-form-toggle__label" :for="selectAllRules">selecionar tudo</label>
</div>
</div>
</div>
<div slot="content">
<div v-for="policyRule in filteredPolicyRules.group" :key="policyRule.name"
class="neo-form-group">
<li v-text="policyRule.name"></li>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="policyRule.name" #click="selectGroupRules(policyRule.rule, policyRule.name, $event)" type="checkbox" v-model="policyRules.name" />
<label class="neo-form-toggle__label" :for="policyRule.name">selecionar grupo</label>
</div>
<div class="neo-form-toggle-list neo-form-toggle-list--inline">
<div v-for="rule in policyRule.rule" :key="rule.title"
class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="rule.title" :value="rule" name="rule" type="checkbox" v-model="checkedRules"/>
<label class="neo-form-toggle__label" :for="rule.title">{{ rule.title }}</label>
<h6 class="neo-text-disabled-options">{{ rule.description }}</h6>
</div>
</div>
</div>
</div>
<div slot="footer">
<span>{{ checkedRules }}</span>
</div>
</slide-out>
</div>
</template>
<script>
import { mapState } from 'vuex';
import SlideOut from '#/components/shared/slideout/SlideOut.vue';
export default {
name: 'ModulePolicyRules',
props: [],
components: {
SlideOut,
},
data() {
return {
isVisible: false,
policyRules: [],
searchQuery: '',
checkedRules: [],
filteredRules: [],
};
},
computed: {
filteredPolicyRules() {
const me = this;
if (this.searchQuery) {
me.filteredRules.pop();
this.policyRules.group.filter((ruleGroup) => {
ruleGroup.rule.forEach((rule) => {
if (rule.title.startsWith(this.searchQuery)) {
console.log(me.filteredRules);
me.filteredRules.push(rule);
}
});
});
console.log(me.filteredRules);
return me.filteredRules;
// return this.policyRules.group.filter(item => item.name.startsWith(this.searchQuery));
}
return this.policyRules;
},
},
methods: {
async loadData() {
const rules = await this.$store.dispatch('policyrules/setPolicyRules');
this.policyRules = rules;
},
},
mounted() {
this.loadData();
},
};
</script>
<style lang="scss">
.neo-form-toggle__label {
text-transform: none;
font-weight: 600;
}
.neo-text-disabled-options {
text-transform: none;
}
</style>
Object expected result using "Rec" in search field:
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
Try this computed prop.
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = [];
rule.forEach(r => {
if (r.title.startsWith(this.searchQuery)) {
rules.push(r);
}
});
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
I'd suggest calling them groups and rules (plural) respectively, to avoid future confusion -- after all they are arrays.
Full demo:
const policyRules = {
"id": "rulesCompany",
"group": [{
"name": "Cadastral",
"rule": [{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
}]
};
new Vue({
el: '#app',
data() {
return {
searchQuery: '',
policyRules
}
},
computed: {
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = rule.filter(this.matchFilter);
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
},
methods: {
matchFilter(item) {
const
search = this.searchQuery.toLowerCase(),
term = (item.title || '').toLowerCase();
return term.includes(search);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="searchQuery" placeholder="Search..." />
<pre>{{ filteredPolicyRules }}</pre>
</div>
So first I've put your list into a map variable. Then I'm filtering that by checking if any of the wanted properties contain the search term. The array.filter Method returns a new array based on what entries returned true and what false.
I'm checking inside name, title and description. Also I made everything lower case so case doesn't matter.
Array.prototype.filter Reference
const term = 'CNE';
console.log(map.filter(e => {
const { name } = e;
if(contains(name, term)) return true;
for(const _r of e.rule) {
const { title, description } = _r;
if(contains(title, term)) return true;
if(contains(description, term)) return true;
}
return false;
}));
function contains(str, term) {
return str.toLowerCase().includes(term.toLowerCase());
}
And I would also suggest like Yom in his answer that you use groups and rules so you can name them better. So that then would be groups.filter(group => {[..]}) and for(const rule of group.rules)
I have a scrolling menu items, and the titles of each item is hardcoded into a const, along side with the id
const list = [
{ name: "category1", id: 0 },
{ name: "category2", id: 1 },
{ name: "category3", id: 2 },
{ name: "category4", id: 3 },
{ name: "category5", id: 4 },
{ name: "category6", id: 5 },
{ name: "category7", id: 6 },
{ name: "category8", id: 7 }
];
I have a json file that contains the category name for each child:
{
"results": [
{
"category": "category1",
"name": {
"title": "mr",
"first": "ernesto",
"last": "roman"
},
"email": "ernesto.roman#example.com",
"id": {
"name": "DNI",
"value": "73164596-W"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/73.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/73.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/73.jpg"
}
},
{
"category": "category2",
"name": {
"title": "mr",
"first": "adalbert",
"last": "bausch"
},
"email": "adalbert.bausch#example.com",
"id": {
"name": "",
"value": null
} etc....
I want to show these categories "category": "category1", as the titles of my menu, I now that I need to start stateless and add them from the JSON, the fetching part from the JSON is done locally in componentDidMount, but I am not sure how can I map them into appearing as menu names to make the menu dynamic, I basically want the same output but from the json not hardcoded. here is a sandbox snippet, would appreciate the help.
https://codesandbox.io/s/2prw4j729p?fontsize=14&moduleview=1
Just convert the JSON output to an object like list with a map function from the results and then set is as MenuItems on the state, which is what you pass to the function on render(). Like that.
import React, { Component } from "react";
import ScrollMenu from "react-horizontal-scrolling-menu";
import "./menu.css";
// One item component
// selected prop will be passed
const MenuItem = ({ text, selected }) => {
return (
<div>
<div className="menu-item">{text}</div>
</div>
);
};
// All items component
// Important! add unique key
export const Menu = list =>
list.map(el => {
const { name, id } = el;
return <MenuItem text={name} key={id} />;
});
const Arrow = ({ text, className }) => {
return <div className={className}>{text}</div>;
};
export class Menucat extends Component {
state = {
selected: "0",
MenuItems: []
};
componentDidMount() {
fetch("menu.json")
.then(res => res.json())
.then(result => {
const items = result.results.map((el, idx) => {
return { name: el.category, id: idx };
});
this.setState({
isLoaded: true,
MenuItems: items
});
});
}
render() {
const { selected, MenuItems } = this.state;
// Create menu from items
const menu = Menu(MenuItems, selected);
return (
<div className="App">
<ScrollMenu
data={menu}
selected={selected}
onSelect={this.onSelect}
alignCenter={true}
tabindex="0"
/>
</div>
);
}
}
export default Menucat;
Cheers!
Looks like you don't have to hard code your category list at all. In your componentDidMount() fetch the json and group the results into separate categories like this:
const json = {
"results": [
{
category: "category1",
name: "Fred"
},
{
category: "category1",
name: "Teddy"
},
{
category: "category2",
name: "Gilbert"
},
{
category: "category3",
name: "Foxy"
},
]
}
const grouped = json.results.reduce((acc, cur) => {
if (!acc.hasOwnProperty(cur.category)) {
acc[cur.category] = []
}
acc[cur.category].push(cur)
return acc;
}, { })
// parent object now has 3 properties, namely category1, category2 and category3
console.log(JSON.stringify(grouped, null, 4))
// each of these properties is an array of bjects of same category
console.log(JSON.stringify(grouped.category1, null, 4))
console.log(JSON.stringify(grouped.category2, null, 4))
console.log(JSON.stringify(grouped.category3, null, 4))
Note that this json has 4 objects in result array, 2 of cat1, and 1 of cat 2 and cat3. You can run this code in a separate file to see how it works. Ofcourse you will be fetching the json object from server. I just set it for demonstration.
Then set teh state:
this.setState({ grouped })
Then in render() you only show the categories that have items like:
const menuBarButtons = Object.keys(this.state.grouped).map((category) => {
/* your jsx here */
return <MenuItem text={category} key={category} onClick={this.onClick} blah={blah}/>
/* or something , it's up to you */
})
I'm assuming you're showing the items based on the currently selected category this.state.selected. So after you have rendered your menu, you would do something like:
const selectedCatItems = this.state.grouped[this.state.selected].map((item) => {
return <YourItem name={item.name} key={item.id} blah={blah} />
})
Then render it:
return (
<div className="app">
<MenuBar blah={blah}>
{menuBarButtons}
</Menubar>
<div for your item showing area>
{selectedCatItems}
</div>
</div>
)
Also, don't forget to change your onClick() so that it sets this.state.selected state properly. I believe you can figure that out yourself.
Hope it helps.
PS: I didn't write a whole copy/paste solution to your problem simply because I'm reluctant to read and understand your UI details and the whole component to component data passing details..
I am trying to write a shopping cart app which when we enter a value in search bar it should update the list and show item available based on value entered.
With the code I wrote, everything is working fine until I made a selection of an item which is not updating the view at first time and showing all the channels.(I think state not updating at first time, but doing at second time)
I am pretty new to react. I tried to add only necessary code here for solution.
state = { open: false, value: '', filteredSections: 'none', displaySections: [] };
filterFunction(e, someSections) {
const filteredSections = [];
this.setState({ value: e.target.value });
someSections.forEach((category) => {
const results = [];
const itemCopy = {...category};
category.arraysinside.forEach((item) => {
if (item.name.toLowerCase().includes(e.target.value.toLowerCase())) { results.push(item); }
});
itemCopy.arraysinside = results;
if (itemCopy.arraysinside.length > 0) { filteredSections.push(itemCopy); }
});
this.setState({ filteredSections: filteredSections });
return filteredSections;
}
updateFunction(displaySections) {
this.setState({ displaySections: displaySections });
}
onChange(opt) {
// makes item selected and changes color of item to know that item is selected
}
render() {
const someSections = customization.arraysinside.filter(c => c.has === 'channels');
let { displaySections, filteredSections } = this.state;
return (
<div>
<FormControl
name="search-items"
placeholder="Search items"
onChange={(e) => {
filteredSections = this.filterFunction(e, someSections);
this.setState({
displaySections: filteredSections.length > 0 ? displaySections = filteredSections : 'No',
value: this.state.value,
});
}}
/>
<div>
{displaySections.length > 0 ? displaySections.map(section =>
<someSections
onChange={(opt) => {
onChange(opt);
this.updateFunction(opt, displaySections);
}}
:
someSections.map(section =>
<someSections
onChange={opt => onChange(opt)}
/>)
}
</div>
</div>
);
}
}
JSON FORMAT WILL BE
{
"name": "product 1",
"has": "channels",
"arraysinside": [
{ "selected": false, "name": "Item 1"},
{ "selected": false, "name": "Item 2"},
{ "selected": false, "name": "Item 3"},
{ "selected": false, "name": "Item 4"},
],
},
{
"name": "product 2",
"has": "channels",
"arraysinside": [
{ "selected": false, "name": "Item 1"},
{ "selected": false, "name": "Item 2"},
{ "selected": false, "name": "Item 3"},
{ "selected": false, "name": "Item 4"},
],
},
displaySections should be updated every time I select the item and should change the view instantly, but with current code, It updates when I enter another value or update selection next time. PLease help, I been trying it for days with no improvement. I will be glad, If suggested a easier way than I wrote.
setState() is asynchronous, so try:
this.setState({ value: e.target.value }, () => { ... do stuff relying on value });
How do I put my props object into an array and iterate through it?
I have a bootstrap navigation bar that has dropdowns. My code scans the javascript object and if the key dropdown is found, it will create a dropdown menu from the data in the javascript object from the dropdown section.
My Javascript Object:
var linksNav = {
items: [
{
"type": "link",
"title": "Events",
"href": "#",
"target": "_self"
},
{
"type": "link",
"title": "Groups",
"href": "#",
"target": "_self",
"dropdown":
{
"dropItems":
[
{
"left": "1st left",
"left-option": ["1","2"]
},
{
"left": "2nd left",
"left-option": ["1","2"]
},
{
"left": "3rd left",
"left-option": ["1","2"]
},
]
}
},
{
"type": "heading",
"title": "Capabilities",
"href": "#",
"target": "_self"
},
]
}
This is my LinksNav class which creates the navigation bar by going through the javascript object. If a dropdown item is detected in my javascript object, it will pass it to my Navsub component:
LinksNav:
var LinksNav = React.createClass({
render: function() {
let navDrop;
if (this.props.isDropdown) {
navDrop = (
<ul className="dropdown-menu fade">
<div>{this.props.isDropdown.dropItems[0].left}></div>
<Navsub isDropdown = {this.props.isDropdown}/>
</ul>
)
}
return (
<li className={this.props.title + ' nav-items'}>
{this.props.title}
{navDrop}
</li>
);
}
});
Navsub:
This is my Navsub class where I try to spit out the data from my dropdown list in my Javascript object if it can find it. :
var Navsub = React.createClass({
render: function() {
var itemsLeft= [];
for (var j = 0; j < this.props.isDropdown.dropItems.length; j++) {
itemsLeft.push(<Navsubrightitems key={j} type={this.props.isDropdown.dropItems[j].left} />);
}
return (
<div className="group-dropdown-menu">
<div className="dropdown-left-menu">
{this.props.isDropdown.dropItems[0].left}
{this.props.isDropdown.dropItems[1].left}
</div>
<div className="dropdown-right-menu">
{itemsRight}
</div>
</div>
);
}
});
I can successfully grab the dropdown list from the json object throuhh props, as seen by my hardcoding:
{this.props.isDropdown.dropItems[0].left}
{this.props.isDropdown.dropItems[1].left}
, but I want to iterate through it so that I get the value of left under dropItems by putting it in an array and then spitting out those values. I attempted to do so by iterating it through this.props.isDropdown.dropItems.length, but seems that's not valid as I get an undefined error.
It seems that the props dropDownItems are not initially present and hence you need to check for and undefined value and then you can use map to iterate over these. You can do it as follows
var Navsub = React.createClass({
render: function() {
return (
<div className="group-dropdown-menu">
<div className="dropdown-left-menu">
{this.props.isDropdown.dropItems[0].left}
{this.props.isDropdown.dropItems[1].left}
</div>
<div className="dropdown-right-menu">
{this.props.isDropdown.dropItems && this.props.isDropdown.dropItems.map((listItem, index) => {
return <Navsubrightitems key={index} type={listItem.left} />
})}
</div>
</div>
);
}
});
I think, this is what you want, Write it like this:
{
this.props.isDropdown.dropItems && this.props.isDropdown.dropItems.map((item,i) => {
return <Navsubrightitems key={i} type={item.left} />
})
}