How to get "name" attribute of a specific mapped file in React? - javascript

Here is the embedded JSX code:
<Col>
<h2> Found Files </h2>
{this.state.foundFiles.map(files => (
<div className = "file-box-search" key={files}>
<input type = "checkbox" name = {files.id}></input>
<p className = ""> <a href = {files.click}> {files.file} </a> </p>
<p> {files.description}</p>
</div>
))}
</Col>
Just for clarity on what's going on here:
this.state.foundFiles is an array object that holds the files I am mapping through, which values such as id and file. An example looks like this:
{
file: 'lesson 1',
id: '1qupvie1LqNdLj-1TZNu3x6-4bT411C4F2YYGSfpc7yk',
description: undefined,
type: 'application/vnd.google-apps.document',
properties: { subject: 'math', grade: 'pre-k' },
parents: [ '1kAzxwEgX5ftI-Sa4nUDK1Y5rJOSJ6VrU' ]
},
The .map() loops through this array and gives several different formatted divs dependent on the values of each file in the array.
Within this .map() method there is a checkbox. When the checkbox is checked, I want to invoke a function that can use the name attribute of that specific checkbox within the map method. I can't just target by id, because then every element within the method would have the same exact id.
What would be the best way to go about doing this?

That's where I suggest to define some constant value with the index:
.map((files, index) => {
// ${`checkbox-${index}-${files.id}`}

Related

How can I add an object to an array inside a Firebase Field Collection?

I'm trying to create a to-do style app. I'm trying to create a card that contains a name and tags field. Here's an example of my data structure. I want to create another cards array but for id: "nhl".
[![enter image description here][1]][1]
I want to create a cards array (everything inside the blue box) but for the 1 object with the id:"nhl".
Here is what my createCard function looks like:
const createCard = async () => {
await updateDoc(doc(db, "users", currentUser.uid), {
labels: arrayUnion({
activeLabel: [
{
name: name,
tags: tags,
},
],
}),
});
getData();
findLabel(activeLabel);
// toast.success("Tags Created!");
console.log("Tags Created");
};
Here is where the function is being called:
return (
<div className="cards">
<div className="cards__create">
<input
onChange={(e) => setName(e.target.value)}
className="cards__input"
placeholder="Enter title..."
></input>
<textarea
onChange={(e) => setTags(e.target.value)}
className="cards__input cards__input--textarea"
placeholder="Enter tags..."
></textarea>
<img
onClick={createCard}
className="cards__add"
src={addIcon}
alt="plus sign icon"
></img>
</div>
arrayUnion() cannot work in your case. As explained in the doc, "each specified element that doesn't already exist in the array will be added to the end."
So, instead of updating the second element of the labels array, arrayUnion() will create a new array element with the same id fiels id:"nhl" and the two cards.
You need to first read the Firestore document, modify the labels Array in your front-end and write it back to Firestore.

Loading a name based on Value from Parent Component Dynamically

Have a Parent Component which certain props such as name, queryValue and image to my Child Component which is a Custom Dropdown where as of now i am displaying the names based on a condition check for the default display in the dropdown, but now more data will be shown along with flags. I need to show the name based on the value from the parent .
Parent.jsx
<SelectCountry
options={[
{
name: 'English (USA)',
queryValue: 'en',
flagIcon: USA,
},
{
name: 'Bahasa Indonesia',
queryValue: 'id',
flagIcon: ID,
},
]}
value={localStorage.getItem('locale')} />
SelectCountry.jsx
<div className={`${baseClassName}__selected-flag-container`}>
<img
src={require(value === "en" ? '../../assets/USA.svg' : '../../assets/indonesia.svg')}
alt='desc'
/>
{value === "en" ? options[0].name : options[1].name}
</div>
//COde for the Custom DropDown will be here
As above, HAve put the condition check and its working since there were only two languages, now when more languages are added, not sure how to pick the name and also setting the src in img tag dynamically.
You are doing all in a bit of wrong way your Parent.jsx is the container component so it should hold all your logic so why not pass the appropriate data.
<SelectCountry
options={[
{
name: 'English (USA)',
queryValue: 'en',
flagIcon: USA,
src: '../../assets/USA.svg'
},
{
name: 'Bahasa Indonesia',
queryValue: 'id',
flagIcon: ID,
src: '../../assets/indonesia.svg'
},
]}
value={localStorage.getItem('locale')}
/>
& in your presentation component SelectCountry.jsx you just present.
const { options } = require('joi');
options.map((option) => {
<div className={`${baseClassName}__selected-flag-container`}>
<img src={option.src} alt='desc' />
{option.name}
</div>;
});
I haven't tested so you might need to adjust the code. Dan has some fantastic free courses on Redux where he teaches more about container & presentation component you can check it out here https://egghead.io/instructors/dan-abramov. Hope this will help you 🙂️🙂️

React-table: save accessor value in state with checkbox

I am trying to use the react-table as a checkbox table. The first column will be the checkboxes, when a checkbox gets selected i want to save the id that was defined in the accessor in the state.
I have been looking at the examples and made my way trough the official documentation, so far without much luck.
My code so far:
import React from 'react';
import ReactTable from "react-table";
import 'react-table/react-table.css'
export default class TableAccessor extends React.Component {
constructor(props){
super(props);
this.state = { personId: null }
//Data to show inside the table, when an item gets selected the ID has to be set in the state
this.data= [
{id: 1, first_name: 'Emma', last_name: 'Smith', email: 'emma#example.com'},
{id: 2, first_name: 'Liam', last_name: 'Miller', email: 'liam#example.com'}
];
//Header data for the table
this.columns = [
{Header: '#', accessor: 'id', Cell: () => <input onChange={() => {
//Here i want to get the ID of the selected item defined in this.data
this.setState({personId: this.data.id});
}} type="checkbox"></input>},
{Header: 'First name', accessor: 'first_name'},
{Header: 'Last name', accessor: 'last_name'},
{Header: 'Email', accessor: 'email'}
];
}
logger = () => {
console.log("personId: " + this.state.personId)
}
render() {
return(
<div className="wrapper">
<button onClick={this.logger}>Print</button>
<ReactTable data={this.data} columns={this.columns} />
</div>
);
}
}
I also want to be able to only check one checkbox at the time. I saw people solving this with radio buttons, however when i change the type="checkbox" to type="radio" i can still select more than one item.
Who can help me overcome this issue and explain me what the best way is to save the accessor value in the state?
Thanks in advance :)
First of all, why would you use react-table package? What is the benefit of using 3rd party package instead of writing it yourself?
Steps I'd take:
Write static HTML table with stuff you need
Map the data array to the table rows and populate it. Make sure to add data-id attribute and pass the id attribute from the array to each element
Add onClick handlers which get the data-id attribute when clicked.
You can use computed property names (example here) and generate state like this: this.setState( {[savedId]: value} )
And that should be it.

Vue warn - Cannot use 'in' operator to search for '[object Array]'

Well,
I'm trying to do a project of an a Shopping cart with vue.js, and the browser Console is showing this error:
vue.common.js:576 [Vue warn]: Error in created hook: "TypeError: Cannot use 'in' operator to search for '[object Array]' in products"
// App.vue
<template>
<div class="container">
<div class="products">
<div class="clearfix">
<product v-for="product in products" :key="product"></product>
</div>
</div>
<div class="shopping-cart">
<shopping-cart></shopping-cart>
</div>
</div>
</template>
<script>
import ShoppingCart from './components/ShoppingCart.vue'
import Product from './components/Product.vue'
export default {
created () {
// dados mockados
var dummy = [
{id: 1, title: 'Name of Product 1', price: 40, image: 'product.png'},
{id: 2, title: 'Name of Product 2', price: 90, image: 'product.png'},
{id: 3, title: 'Name of Product 3', price: 10, image: 'product.png'},
{id: 4, title: 'Name of Product 4', price: 20, image: 'product.png'}
];
this.$set('products', dummy)
},
data () {
return {
products: []
}
},
components: { Product, ShoppingCart }
}
</script>
What can I do?
I tried a lot of things and still without success =(
First of all you component name in template is "product" and also the key in for loop is also "product". Either you change Component name to suitable name like.
And you must have forgot to give a name(assign a name of component for tepmplate) to component which you imported. You cannot use imported component just like that without giving it reference name to use it in template.
components: { Product:productName, ShoppingCart: shoppingCart }
This way you use <product-name> </product-name> in template and so after that in for loop, the product in prodcuts will work.
Also products array should not work with this way. It should be in computed hook.
computed ={}
Or I should suggest you should directly asssign it in data()
for better working , in the $set method in VUE
the first arg for pass 'this' keyword
some thing like this
this.$set(this,'your_object', value)
and notice second arg must be String
you must use
this.products = dummy
instead of
this.$set('products', dummy)
and if you create your array in mounted () better than created () in your single app
I think the problem is with $set method, you need to specify the object as 1st parameter, see full doc here
so you need to do something like this:this.$set(this.products, dummy)
also this will not give you 4 products in the v-for loop. I would suggest to assign the products directly in data()

How to get a reference to target element inside a callback

I'm building a component which displays a series of generic input fields. The backing store uses a simple array of key-value pairs to manage the data:
[
{fieldkey: 'Signs and Symptoms', value:'fever, rash'},
{fieldkey: 'NotFeelingWell', value:'false'},
{fieldkey: 'ReAdmission', value:'true'},
{fieldkey: 'DateOfEvent', value:'12/31/1999'}
]
In order to eliminate a lot of boilerplate code related to data binding, the component uses these same keys when generating the HTML markup (see 'data-fieldname' attribute).
var Fields = React.createClass({
handleOnChange:function(e){
Actions.updateField( {key:e.target.attributes['data-fieldname'].value, value:e.target.value})
},
setValue:function(){
var ref = //get a reference to the DOM element that triggered this call
ref.value = this.props.form.fields[ref.attributes['data-fieldname']]
},
render:function(){
return (<div className="row">
<Input data-fieldname="Signs and Symptoms" type="text" label='Notes' defaultValue="Enter text" onChange={this.handleOnChange} value={this.setValue()} />
<Input data-fieldname="NotFeelingWell" type="checkbox" label="Not Feeling Well" onChange={this.handleOnChange} value={this.setValue()} />
<Input data-fieldname="ReAdmission" type="checkbox" label="Not Feeling Great" onChange={this.handleOnChange} value={this.setValue()} />
<Input data-fieldname="DateOfEvent" type="text" label="Date Of Event" onChange={this.handleOnChange} value={this.setValue()} />
</div>)
}
})
My goal is to use the same two functions for writing/reading from the store for all inputs and without code duplication (i.e. I don't want to add a refs declaration to each input that duplicates the key already stored in 'data-fieldname') Things work swimmingly on the callback attached to the 'onChange' event. However, I'm unsure how to get a reference to the DOM node in question in the setValue function.
Thanks in advance
I'm not sure if I understand your question right, but to reduce boilerplate I would map your array to generate input fields:
render:function(){
var inputs = [];
this.props.form.fields.map(function(elem){
inputs.push(<Input data-fieldname={elem.fieldkey} type="text" label="Date Of Event" onChange={this.handleOnChange} value={elem.value} />);
});
return (<div className="row">
{inputs}
</div>)
}
This will always display your data in props. So when handleOnChange gets triggered the component will rerender with the new value. In my opinion this way is better than accessing a DOM node directly.
If you want to use dynamic information on the input, you need to pass it through the array, and make a loop.
Here is a little example based on Dustin code:
var fieldArray = [ //replace by this.props.form.fields
{
fieldkey: 'Signs and Symptoms',
value: 'fever, rash',
type: 'text',
label: 'Notes'
},
{
fieldkey: 'NotFeelingWell',
value: 'false',
type: 'checkbox',
label: 'Not Feeling Well'
},
];
var Fields = React.createClass({
handleOnChange:function(e){
var fieldKey = e.target.attributes['data-fieldname'].value;
Actions.updateField({
key: fieldKey,
value: e.target.value
})
},
render() {
var inputs = [];
fieldArray.map(function(field) { //replace by this.props.form.fields
inputs.push(
<Input
data-fieldname={field.fieldkey}
value={field.value}
type={field.type}
label={field.label}
onChange={this.handleOnChange} />
);
}.bind(this));
return (
<div className="row">
{inputs}
</div>
);
}
});

Categories

Resources