I am trying to render an icon only if collection.masterAssetId === asset.id. I am using a getMaster function to find the collection.masterAssetId. The problem that I am having is that the collectionsarr can be undefined in my getMaster function, but also return a number. I have this error: Uncaught TypeError: Cannot read property 'masterAssetId' of undefined. Would be great if someone could fix this error for me?
Secondly, I am probably making this way too complicated, so feel free to suggest a different approach.
App.js
import React from 'react';
import './App.css';
import {collections} from "./data.js"
import {assets} from "./data.js"
import {FontAwesome, FaStar} from "react-icons/fa"
class App extends React.Component {
constructor() {
super()
this.state = {
collectionsarr: collections,
assetsarr: assets,
clickedassets: []
}
}
getMasterId(assetnr){
const assetnum = ""
if(this.state.collectionsarr.filter(element => element.masterAssetId === assetnr) === undefined){
const assetnum = 0
} else {
const assetnum = this.state.collectionsarr.filter(element => element.masterAssetId === assetnr)[0].masterAssetId
}
return assetnum
}
render(){
return (
<div className="App">
<h1>Sitecore coding challenge</h1>
<div className="left">
{this.state.collectionsarr.map(element =>
<div key={element.id}>
<p onClick={()=>this.handleAssetsClick(element.id)}>{element.name}</p>
<img src={this.getAssetPath(element.masterAssetId)} alt="pic"/>
<br></br>
</div>
)}
</div>
<div className="right">
{this.state.clickedassets.map(asset =>
<div key={asset.id}>
<img src={require(`./${asset.path}`)} alt="pic"/>
<p>{asset.name}</p>
<p>{asset.id}</p>
<button onClick={() => this.makeMaster(asset.id)}>Make master!</button>
<p>icon "this is the master</p>
{asset.id === this.getMasterId(asset.id) && <FaStar />}
<br></br>
</div>
)}
</div>
</div>
)
}
}
export default App
data.js
const collections = [
{
id: 1,
name: "The Simpsons",
masterAssetId: 13,
tags: {
name: "Cartoon",
subTag: {
name: "Simpsons family",
subTag: {
name: "2014",
},
},
},
},
{
id: 2,
name: "Super heroes",
masterAssetId: 24,
tags: {
name: "DC Super heroes",
subTag: {
name: "2014",
},
},
},
{
id: 3,
name: "Toy story",
masterAssetId: 31,
tags: {
name: "Disney",
subTag: {
name: "Pixar",
subTag: {
name: "Original movie",
subTag: {
name: "2010",
},
},
},
},
},
{
id: 4,
name: "Ninjago",
masterAssetId: 42,
tags: {
name: "Ninja",
subTag: {
name: "Secret Ninja Force",
subTag: {
name: "2017",
},
},
},
},
];
const assets = [
{
id: 11,
name: "Homer Simpson",
path: "Homer.jpg",
collectionId: 1,
},
{
id: 12,
name: "Lisa Simpson",
path: "Lisa.jpg",
collectionId: 1,
},
{
id: 13,
name: "Bart Simpson",
path: "Bart.jpg",
collectionId: 1,
},
{
id: 14,
name: "Marge Simpson",
path: "Marge.jpg",
collectionId: 1,
},
{
id: 15,
name: "Grampa Simpson",
path: "Grampa.jpg",
collectionId: 1,
},
{
id: 16,
name: "Maggie Simpson",
path: "Maggie.jpg",
collectionId: 1,
},
{
id: 21,
name: "Green Lantern",
path: "Green lantern.jpg",
collectionId: 2,
}
];
Is the collections data being set properly? What's the console.log() output of the state at render?
At any rate, I think the better approach for setting state is to do it in componentDidMount() lifecycle method:
import {getCollection} from "./data.js";
state = {
collectionsarr: [],
assetsarr: [],
clickedassets: []
},
componentDidMount = () => {
this.setState({
collectionsarr: getCollection()
});
}
data.js:
const collections = [...];
export function getCollection() {
return collections;
}
Change your getMasterId function
showIcon(assetnr) {
let index = (this.state.collectionsarr || []).findIndex(item => {
return item.masterAssetId === assetnr;
})
return index != -1;
}
This function will return true if assetId === masterId then You can render it as
{this.showIcon(asset.id) && <FaStar />}
Related
I do have a problem on my code. What I am trying to do, is that when I want to click on a button, I want that this button become "HIDE" (it's show in the beginning), and show the specific Id's information. I tryed to do it with many example but the others buttons change this specific button too. Then, I didn't find the issue. If someone can help me with it. Thank you very much.
import React, { Component } from "react";
import "./IlkOnbir.css";
export default class IlkOnbir extends Component {
state = {
info: false,
btn: true,
players: [
{
id: 1,
name: "Altay Bayindir",
age: "21",
mevki: "Kaleci",
},
{
id: 2,
name: "Serdar Aziz",
age: "29",
mevki: "Stoper",
},
{
id: 3,
name: "Simon Falette",
age: "23",
mevki: "Stoper",
},
{
id: 4,
name: "Nabil Dirar",
age: "33",
mevki: "Sag Bek",
},
{
id: 5,
name: "Hasan Ali Kaldirim",
age: "32",
mevki: "Sol Bek",
},
{
id: 6,
name: "Emre Belözoglu",
age: "37",
mevki: "Orta Saha",
},
{
id: 7,
name: "Luiz Gustavo",
age: "32",
mevki: "Orta Saha",
},
{
id: 8,
name: "Ozan Tufan",
age: "25",
mevki: "Orta Saha",
},
{
id: 9,
name: "Deniz Türüç",
age: "28",
mevki: "Orta Saha",
},
{
id: 10,
name: "Gary Rodriguez",
age: "29",
mevki: "Kanat",
},
{
id: 11,
name: "Tolga Cigerci",
age: "29",
mevki: "Orta Saha",
},
{
id: 12,
name: "Ferdi",
age: "21",
mevki: "Stoper",
},
{
id: 13,
name: "Mevlüt",
age: "33",
mevki: "Stoper",
},
{
id: 14,
name: "Vedat",
age: "26",
mevki: "Stoper",
},
],
}
showInfo = (i) => {
this.setState({ info: !this.state.info});
}
handleClick = event => {
event.currentTarget.classList.toggle('active');
}
btnDisplay = (e) => {
console.log(e.currentTarget.id);
this.setState({info: !this.state.info, btn:!this.state.btn});
}
render() {
const hideInfo = this.state.info;
const pl = this.state.players.map((player,i) => {
return (
<div key={i} className="card--content movie" onClick={this.handleClick}>
<div className="content">
<img src={player.source} alt="" />
<div class="buttons">
<p
onClick={() => this.showInfo(player[i])} key={player[i]} className="btn effect04 movie" data-sm-link-text="INFO"><span>{this.state.btn === true ? "SHOW" : "HIDE"}</span>
</p>
{hideInfo === true ? (
<div className="opening">
<p>{player.name}</p>
<p>{player.age} </p>
<p>{player.mevki} </p>
</div>
) : (
""
)}
</div>
</div>
</div>
)})
return (
<div className="container">
<section className="card">
{pl}
</section>
</div>
)}}
Each player needs its own info property; right now info applies to all of them.
See working example:
https://codesandbox.io/s/sweet-jang-m3yfe?file=/src/App.js
{
id: 1,
name: "Altay Bayindir",
age: "21",
mevki: "Kaleci",
info: false
}
showInfo = i => {
this.setState(state => ({
...state,
players: state.players.map((player, index) =>
index === i ? { ...player, info: !player.info } : player
)
}));
};
onClick={() => this.showInfo(i)}
{player.info && (
<div className="opening">
<p>{player.name}</p>
<p>{player.age} </p>
<p>{player.mevki} </p>
</div>
)}
I´m doing some beginner practise on React.js and created a tree view component using material-ui. Now I want to implement a search bar to search for the entered keyword in the tree view.
Here is my sample data:
[
{
id: 1,
name: "Item 1",
children: [
{
id: 2,
name: "Subitem 1",
children: [
{
id: 3,
name: "Misc 1",
children: [
{
id: 4,
name: "Misc 2"
}
]
}
]
},
{
id: 5,
name: "Subitem 2",
children: [
{
id: 6,
name: "Misc 3",
}
]
}
]
},
{
id: 7,
name: "Item 2",
children: [
{
id: 8,
name: "Subitem 1",
children: [
{
id: 9,
name: "Misc 1"
}
]
},
{
id: 10,
name: "Subitem 2",
children: [
{
id: 11,
name: "Misc 4"
},
{
id: 12,
name: "Misc 5"
},
{
id: 13,
name: "Misc 6",
children: [
{
id: 14,
name: "Misc 7"
}
]
}
]
}
]
}
]
The rendering part is working as expected.
const getTreeItemsFromData = treeItems => {
return treeItems.map(treeItemData => {
let children = undefined;
if (treeItemData.children && treeItemData.children.length > 0) {
children = getTreeItemsFromData(treeItemData.children);
}
return(
<TreeItem
key={treeItemData.id}
nodeId={treeItemData.id}
label={treeItemData.name}
children={children}/>
);
});
};
const DataTreeView = ({ treeItems }) => {
return(
<TreeView
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
{getTreeItemsFromData(treeItems)}
</TreeView>
);
};
class App extends Component {
render() {
return (
<div className="App">
<DataTreeView treeItems={searchedNodes} />
</div>
);
}
}
Now I´m struggeling to implement the search functionality. I want to use the material-search-bar (https://openbase.io/js/material-ui-search-bar)
This solution assumes that you use unique id for each item in the tree.
It uses Depth first search algorithm.
Before trying please fix your sample data non-unique IDs. I didn't notice them at first and wasted time debugging for no bug.
function dfs(node, term, foundIDS) {
// Implement your search functionality
let isMatching = node.name && node.name.indexOf(term) > -1;
if (Array.isArray(node.children)) {
node.children.forEach((child) => {
const hasMatchingChild = dfs(child, term, foundIDS);
isMatching = isMatching || hasMatchingChild;
});
}
// We will add any item if it matches our search term or if it has a children that matches our term
if (isMatching && node.id) {
foundIDS.push(node.id);
}
return isMatching;
}
function filter(data, matchedIDS) {
return data
.filter((item) => matchedIDS.indexOf(item.id) > -1)
.map((item) => ({
...item,
children: item.children ? filter(item.children, matchedIDS) : [],
}));
}
function search(term) {
// We wrap data in an object to match the node shape
const dataNode = {
children: data,
};
const matchedIDS = [];
// find all items IDs that matches our search (or their children does)
dfs(dataNode, term, matchedIDS);
// filter the original data so that only matching items (and their fathers if they have) are returned
return filter(data, matchedIDS);
}
I'm trying to filter a on a nested array inside an array of objects in an Angular app. Here's a snippet of the component code -
var teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
What I'm trying to achieve is if I search for m5 for example my result should be -
var teams = [
{ name: 'Team1', members: [] },
{ name: 'Team2', members: [{ name: 'm5' }] },
{ name: 'Team3', members: [] }
];
So I've got teams and filteredTeams properties and in my search function I'm doing -
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = this.teams.map(t => {
t.members = t.members.filter(d => d.name.toLowerCase().includes(value));
return t;
})
}
Now this does work to some extent however because I'm replacing the members it's destroying the array on each call (if that makes sense). I understand why this is happening but my question is what would be the best way to achieve this filter?
you were very close, the only thing that you did wrong was mutating the source objects in teams
basically you can use spread operator to generate a new entry and then return a whole new array with new values.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const value = 'm5';
const result = teams.map(t => {
const members = t.members.filter(d => d.name.toLowerCase().includes(value));
return { ...t, members };
})
console.log(result)
Check this. Instead of hard coded m5 pass your value.
const teams = [
{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] },
{ name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] },
{ name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }
];
const filteredTeams = teams.map(team => ({ name: team.name, members: team.members.filter(member => member.name.includes('m5')) }));
console.log(filteredTeams);
You are mutating the original objects, but you could assing new properties to the result object for mapping instead.
var teams = [{ name: 'Team1', members: [{ name: 'm1' }, { name: 'm2' }, { name: 'm3' }] }, { name: 'Team2', members: [{ name: 'm4' }, { name: 'm5' }, { name: 'm6' }] }, { name: 'Team3', members: [{ name: 'm7' }, { name: 'm8' }, { name: 'm9' }] }],
result = teams.map(o => Object.assign(
{},
o,
{ members: o.members.filter(({ name }) => name === 'm5') }
));
console.log(result);
console.log(teams);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try to seperate your filter function first:
const filterTeamMembers = (teams, filterArr) => {
const useFilter = filterArr.map(x => x.toLowerCase());
return teams.map(team => ({
...team,
members: team.members.filter(member => useFilter.includes(member.name))
}))
};
// =========== And then:
onSearchChange(event: any): void {
let value = event.target.value;
this.filteredTeams = filterTeamMembers(this.teams, [value]);
}
I'm working on a simple table using reactjs and ant design.
My plan is to add and remove a new item on the list on button click.
My problem is I don't know how to do that.
I tried to follow this thread but no luck.
Hope you understand me.
Thanks.
sample code
function remove() {
console.log("remove");
}
function add() {
console.log("add");
}
const columns = [
{
title: "Num",
dataIndex: "num"
},
{
title: "Name",
dataIndex: "name"
},
{
title: "Age",
dataIndex: "age"
},
{
title: "Address",
dataIndex: "address"
}
];
const data = [
{
key: "1",
num: 1,
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park"
},
{
key: "2",
num: 2,
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park"
},
{
key: "3",
num: 3,
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park"
}
];
<Table pagination={false} columns={columns} dataSource={data} />
<Button type="primary" onClick={add}>
add
</Button>
<Button type="danger" onClick={remove}>
remove
</Button>
You need to use react state. State holds the data, when you want to add or remove, update this state and react with re-render the table.
I have updated your code. On click of add a new random row is added. On click of remove last row is removed.
CodeSandbox
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Table, Button } from "antd";
function remove() {
console.log("remove");
}
const columns = [
{
title: "Num",
dataIndex: "num"
},
{
title: "Name",
dataIndex: "name"
},
{
title: "Age",
dataIndex: "age"
},
{
title: "Address",
dataIndex: "address"
}
];
let data = [
{
key: "1",
num: 1,
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park"
},
{
key: "2",
num: 2,
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park"
},
{
key: "3",
num: 3,
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park"
}
];
export default class MyTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: data
};
}
add = () => {
var row = {
key: "99",
num: 99,
name: "I am New",
age: 32,
address: "New Address"
};
var newStateArray = [...this.state.data];
newStateArray.push(row);
this.setState(() => {
return {
data: newStateArray
};
});
}
remove = () => {
var newStateArray = [...this.state.data];
if(newStateArray.length > 1) {
newStateArray.pop();
}
this.setState(() => {
return {
data: newStateArray
};
});
}
render() {
return (
<div>
<Table
pagination={false}
columns={columns}
dataSource={this.state.data}
/>
<Button type="primary" onClick={this.add}>
add
</Button>
<Button type="danger" onClick={this.remove}>
remove
</Button>
</div>
);
}
}
ReactDOM.render(<MyTable />, document.getElementById("container"));
I'm trying to use filter to remove an object from the array. When I click on the recently added item it will console.log the items id but doesn't remove the item from the array, not sure where I'm going wrong?
import React, { Component } from 'react'
import { reduxForm } from 'redux-form'
// import Input from '../forms/Input/Input'
import actions from './actions'
import { connect } from 'react-redux'
import styles from './Catalogue.styl'
// import Checklist from './Checklist/Checklist'
#reduxForm({
form: 'orderReview',
})
#connect(null, actions)
export default class Catalogue extends Component {
constructor() {
super()
this.state = {
filterText: '',
favourites: [],
data: [
{ id: 1, label: 'baguettes' },
{ id: 2, label: 'bread' },
{ id: 3, label: 'potatoes' },
{ id: 4, label: 'rice' },
{ id: 5, label: 'pasta' },
{ id: 6, label: 'chicken' },
{ id: 7, label: 'beans' },
{ id: 8, label: 'apples' },
{ id: 9, label: 'oranges' },
{ id: 10, label: 'grapes' },
{ id: 11, label: 'biscuits' },
],
}
}
handleFilterUpdate = event => {
this.setState({
filterText: event.target.value,
})
}
addFavourite = (id) => {
const favList = this.state.favourites.concat([id])
this.setState({
favourites: favList,
})
console.log(id)
}
removeFavourite = (id) => {
console.log(id)
const removeFav = this.state.favourites.filter((_, i) => i !== id)
this.setState({
favourites: removeFav,
})
}
render() {
const {
data,
filterText,
favourites,
} = this.state
const NamesList = props => (
<div>
{props.data.filter(items => {
return items.label.toLowerCase().indexOf(filterText.toLowerCase()) >= 0
})
.map(item => {
return (
<div
key={item.id}
onClick={() => props.addFavourite(item.id)}
>
{item.label}
</div>
)
}
)
}
</div>
)
const SaveName = props => {
const idList = props.favourites.map(id => {
const { label } = data[id]
return (
<div>
<br />
<li key={id} onClick={() => props.removeFavourite(data[id])}>{label}</li>
</div>
)
})
return (
<div>{idList}</div>
)
}
return (
<div>
<div className={styles.filtersList}>
<ul className={styles.filtersUl}>
<li className={styles.filtersLi}>znacky</li>
<li className={styles.filtersLi}>zeme</li>
<li className={styles.filtersLi}>Specialni</li>
</ul>
</div>
<input
type="text"
value={filterText}
onChange={this.handleFilterUpdate}
placeholder="Hledat podle nazvu"
/>
<NamesList data={data} addFavourite={this.addFavourite}/>
{filterText}
<SaveName favourites={favourites} removeFavourite={this.removeFavourite} />
</div>
)
}
}
You are iterating through your entire array, and you compare the parameter id with the index of the currently processed item of the array.
Instead, compare the item.id with the parameter, not with i:
class MyApp extends React.Component {
constructor() {
super()
this.state = {
favourites: [
{ id: 1, label: 'baguettes' },
{ id: 2, label: 'bread' },
{ id: 3, label: 'potatoes' },
{ id: 4, label: 'rice' },
{ id: 5, label: 'pasta' },
{ id: 6, label: 'chicken' },
{ id: 7, label: 'beans' },
{ id: 8, label: 'apples' },
{ id: 9, label: 'oranges' },
{ id: 10, label: 'grapes' },
{ id: 11, label: 'biscuits' },
],
}
}
removeFavourite = (id) => {
const removeFav = this.state.favourites.slice();
removeFav.splice(id, 1);
this.setState({
favourites: removeFav
})
}
render() {
return(
<ul>
{this.state.favourites.map((item, i) => <li key={item.id}>{item.label} <button onClick={this.removeFavourite.bind(this, i)}>Remove</button></li>)}
</ul>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("myApp"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="myApp"></div>
Alternatively, you could also just use splice() to remove the item directly:
class MyApp extends React.Component {
constructor() {
super()
this.state = {
favourites: [
{ id: 1, label: 'baguettes' },
{ id: 2, label: 'bread' },
{ id: 3, label: 'potatoes' },
{ id: 4, label: 'rice' },
{ id: 5, label: 'pasta' },
{ id: 6, label: 'chicken' },
{ id: 7, label: 'beans' },
{ id: 8, label: 'apples' },
{ id: 9, label: 'oranges' },
{ id: 10, label: 'grapes' },
{ id: 11, label: 'biscuits' },
],
}
}
removeFavourite = (id) => {
const removeFav = this.state.favourites.filter(item => item.id-1 != id)
this.setState({
favourites: removeFav
})
}
render() {
return(
<ul>
{this.state.favourites.map((item, i) => <li key={item.id}>{item.label} <button onClick={this.removeFavourite.bind(this, i)}>Remove</button></li>)}
</ul>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("myApp"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="myApp"></div>