Display previously uploaded files or images - javascript

I have two data tables and am using Django at the back end and Vuejs at the front end.
(1) Products, stores the product details and a single image.
(2) Products_Images, stores the multiple images with relation to the product.
The below code is called from < Product List > when Editing the product details including adding images or removing images.
My problem is that in Edit mode I need the previously selected displayed instead of ( No Files Chosen )
<!-- Edit a Product (Start) -->
<template id="product-edit">
<div>
<h2>Product (Edit)</h2>
<form method="post" enctype="multipart/form-data" ref="itemMaster">
<!-- Display Product Name -->
<div class="form-group">
<input class="form-control" id="edit-name" v-model="product.itemfullhd" required/>
</div>
<!-- Upload Single Image -->
<div class="form-group">
<!-- ### MY PROBLEM HERE ### -->
<label for="edit-imagemain">Main Image </label>
<input type="file" id="edit-imagemain" v-model="product.Image_file" #change="onFileChanged" required/>
<img class="cart-thumbnail" v-bind:src="'/media/' + product.image_file" alt="image"/>
</div>
<!-- Upload Multiple Images -->
<div class="form-group">
<!-- ### MY PROBLEM ALSO HERE ### -->
<label for="files">Xtra Images</label>
<input type="file" id="files" ref="files" multiple v-on:change="handleFilesUpload()"/>
<div>
<!-- Display the Multiple Images -->
<table class="table table-striped ">
<thead>
<tr>
<th>Xtra Image File/s</th>
<th>Image</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="imagerec in products_images" and v-if="( imagerec.itemfullhd == product.itemfullhd )" style="clear: both;">
<td>/media/{{imagerec.images_multiple}}</td>
<td> <img class="cart-thumbnail" v-bind:src="'/media/' + imagerec.images_multiple" alt="image"/> </td>
<td> <input type="checkbox" :value="imagerec.mark" number> </td>
</tr>
</tbody>
</table>
</div>
</div>
<button type="submit" class="btn btn-primary" #click.prevent="updateProduct">Save</button>
<a class="btn btn-dark"><router-link to="/product-list">Cancel</router-link></a>
</form>
</div>
</template>
<!-- Edit a Product (Done) -->
<script>
var store = new Vuex.Store({
state: {
products: [],
products_images: [],
},
mutations: {
loadProducts: (state, {products, products_images}) => {
state.products = products;
state.products_images = products_images;
},
},
actions: {
retrieveProducts: ({ commit }) => {
axios.get('/biggmount_home/Show-Items-Axios')
.then(response => {
products = response.data.md_items_qs2; // send by views.py
products_images = response.data.md_items_images_qs2; // send by views.py
commit('loadProducts', {products, products_images})
})
.catch(error => {
alert("ERROR")
commit('ERRORED', error)
})
// AXIOS method - End
},
},
})
function findProduct (productId) {
return products[findProductKey(productId)];
};
function findProductKey (productId) {
for (var key = 0; key < products.length; key++) {
if (products[key].id == productId) {
return key;
}
}
};
var ProductEdit = Vue.extend({
template: '#product-edit',
data: function () {
return {
product: findProduct(this.$route.params.product_id),
selectedImage: null,
// image_file: product.image_file,
// files: products_images.images_multiple,
selected: [],
};
},
methods: {
onFileChanged (event) {
this.selectedImage = event.target.files[0]
this.product.image_file = this.selectedImage.name
},
/* Handles a change on the file upload */
handleFilesUpload(){
this.files = this.$refs.files.files;
this.image_file = this.$refs.files.files;
},
updateProduct: function () {
let product = this.product;
const formData = new FormData()
if (this.selectedImage != null) {
formData.append('Item Image', this.selectedImage, this.selectedImage.name)
} else {
formData.append('Item Image', [])
}
if (this.files != null) {
/* Iterate over any file sent over appending the files to the form data. */
for( var i = 0; i < this.files.length; i++ ){
let file = this.files[i];
formData.append('Item Images', file);
}
} else {
formData.append('Item Images', [])
}
formData.append('Item Id', product.id)
formData.append('Item Name', product.itemfullhd)
formData.append('Item Imagefl', product.imagefl)
axios.post('/biggmount_home/Post-Items-Axios', formData)
products[findProductKey(product.id)] = {
id: product.id,
itemfullhd: product.itemfullhd,
imagefl: product.imagefl,
image_file: '/biggmount_shop/images/' + this.selectedImage.name,
std_rte: product.std_rte,
};
router.push('/product-list');
},
}
});
const app = new Vue({
router,
store,
methods: {
...Vuex.mapMutations([
'loadProducts',
]),
...Vuex.mapActions([
'retrieveProducts'
]),
},
computed: {
...Vuex.mapState([
'products',
'products_images',
]),
},
mounted() {
this.retrieveProducts()
},
}).$mount('#app')
</script>

Related

Adding the column name in the table and links in vue

I am beginner web developer. I make my first crud in Laravel 8 and Vue.
I use this component t in my project: https://www.npmjs.com/package/vuejs-datatable
I have this code:
DataTable.vue:
<template>
<div>
<div class="row mb-3">
<div class="col-3">
<div class="input-group">
<input
v-model="search"
class="form-control"
placeholder="Szukaj..."
type="text"
>
<div class="input-group-append">
<button class="btn btn-primary" type="button" #click.prevent="handleSearch">
<font-awesome-icon icon="fas fa-search" />
</button>
</div>
</div>
</div>
<div class="col-2">
<div class="input-group">
<label for="pageOption" class="mt-2 mr-2">Na stronie</label>
<select class="form-control" v-model="perPage" #change="handlePerPage" id="pageOption">
<option v-for="page in pageOptions" :key="page" :value="page">{{ page }}</option>
</select>
</div>
</div>
</div>
<table class="table table-hover">
<thead>
<tr>
<th class="table-head">#</th>
<th v-for="column in columns" :key="column" #click="sortByColumn(column)"
class="table-head">
{{ column | columnHead }}
<span v-if="column === sortedColumn">
<font-awesome-icon v-if="order === 'asc' " icon="fas fa-angle-up" />
<font-awesome-icon v-else icon="fas fa-angle-down" />
</span>
</th>
</tr>
</thead>
<tbody>
<tr class="" v-if="tableData.length === 0">
<td class="lead text-center" :colspan="columns.length + 1">Brak danych do wyświetlenia.</td>
</tr>
<tr v-for="(data, key1) in tableData" :key="data.id" class="m-datatable__row" v-else>
<td>{{ serialNumber(key1) }}</td>
<td v-for="(value, key) in data">{{ value }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script type="text/ecmascript-6">
import axios from 'axios';
import Vue from 'vue';
import 'vuejs-datatable/dist/themes/bootstrap-4.esm';
import {
VuejsDatatableFactory,
IDataFnParams,
IDisplayHandlerParam,
ITableContentParam,
TColumnsDefinition,
VueDatatable
} from 'vuejs-datatable';
Vue.use(VuejsDatatableFactory, VueDatatable);
export default {
props: {
fetchUrl: {type: String, required: true},
columns: {type: Array, required: true},
},
data() {
return {
tableData: [],
url: '',
pagination: {
meta: {to: 1, from: 1}
},
offset: 4,
currentPage: 1,
perPage: 1,
sortedColumn: this.columns[0],
order: 'asc',
search: null,
pageOptions: [1, 10, 20, 50],
}
},
watch: {
fetchUrl: {
handler: function (fetchUrl) {
this.url = fetchUrl
},
immediate: true
}
},
created() {
return this.fetchData()
},
computed: {
/**
* Get the pages number array for displaying in the pagination.
* */
pagesNumber() {
if (!this.pagination.meta.to) {
return []
}
let from = this.pagination.meta.current_page - this.offset
if (from < 1) {
from = 1
}
let to = from + (this.offset * 2)
if (to >= this.pagination.meta.last_page) {
to = this.pagination.meta.last_page
}
let pagesArray = []
for (let page = from; page <= to; page++) {
pagesArray.push(page)
}
return pagesArray
},
/**
* Get the total data displayed in the current page.
* */
totalData() {
return (this.pagination.meta.to - this.pagination.meta.from) + 1
}
},
methods: {
fetchData() {
let dataFetchUrl = `${this.url}&page=${this.currentPage}&column=${this.sortedColumn}&order=${this.order}&per_page=${this.perPage}`
axios.get(dataFetchUrl)
.then(({data}) => {
this.pagination = data
this.tableData = data.data
}).catch(error => this.tableData = [])
},
/**
* Get the serial number.
* #param key
* */
serialNumber(key) {
return (this.currentPage - 1) * this.perPage + 1 + key
},
/**
* Change the page.
* #param pageNumber
*/
changePage(pageNumber) {
this.currentPage = pageNumber
this.fetchData()
},
/**
* Sort the data by column.
* */
sortByColumn(column) {
if (column === this.sortedColumn) {
this.order = (this.order === 'asc') ? 'desc' : 'asc'
} else {
this.sortedColumn = column
this.order = 'asc'
}
this.fetchData()
},
handleSearch() {
this.fetchData()
},
handlePerPage($event) {
this.perPage = $event.target.value;
this.fetchData()
}
},
filters: {
columnHead(value) {
return value.split('_').join(' ').toUpperCase()
}
},
translate: {
nextButton: 'Dalej',
previousButton: 'Wstecz',
placeholderSearch: 'Szukaj...',
},
name: 'DataTable'
}
</script>
<style scoped>
</style>
Notes.vue:
<template>
<CRow>
<CCol col="12">
<transition name="slide">
<CCard>
<CCardBody>
<h4>
Menus
</h4>
<CButton color="success" #click="createNote()" class="mb-3 my-5">Add Menu <font-awesome-icon icon="fas fa-plus" /> </CButton>
<div class="flex-center position-ref full-height">
<data-table
:fetch-url="datatTableUrl"
:columns="['name', 'email', 'id' , 'created_at']"
:headers="['nazwa', 'adres email', 'ID' , 'utworzono']"
></data-table>
</div>
</CCardBody>
</CCard>
</transition>
</CCol>
</CRow>
</template>
<script>
import Vue from 'vue';
export default {
data() {
return {
datatTableUrl: '',
}
},
created: function () {
this.datatTableUrl = Vue.prototype.$apiAdress + '/api/users/dataTable' + '?token=' + localStorage.getItem("api_token");
},
methods: {
noteLink(id) {
return `notes/${id.toString()}`
},
editLink(id) {
return `notes/${id.toString()}/edit`
},
showNote(id) {
const noteLink = this.noteLink(id);
this.$router.push({path: noteLink});
},
editNote(id) {
const editLink = this.editLink(id);
this.$router.push({path: editLink});
},
deleteNote(id) {
let self = this;
let noteId = id;
axios.post(this.$apiAdress + '/api/notes/' + id + '?token=' + localStorage.getItem("api_token"), {
_method: 'DELETE'
})
.then(function (response) {
self.message = 'Successfully deleted note.';
self.showAlert();
self.getNotes();
}).catch(function (error) {
console.log(error);
self.$router.push({path: '/login'});
});
},
createNote() {
this.$router.push({path: 'notes/create'});
},
}
}
</script>
This code work fine.
I have 2 problems:
I would like the column headers in the table to be taken from: headers - currently these are the column names from the database (ie: columns).
I would like to add a link to edit and delete a record. I have created methods: editLink (), deleteNote (). How can I add them to my table? I would like them to be visible next to the "name" column
How can I make it?
Please help me :)
For the problem #1. I would do it this way.
Merge the columns and the headers as one Object, ex: where the key will be the column name (Important: don't forget to register the headers prop).
<data-table
:fetch-url="datatTableUrl"
:headers="{'name': 'nazwa','email': 'adres email','id': 'ID' , 'created_at': 'utworzono'}"
></data-table>
In the DataTable:
<th v-for="(column, label) in headers" :key="column" #click="sortByColumn(column)" class="table-head">
{{ label | columnHead }}
<span v-if="column === sortedColumn">
<font-awesome-icon v-if="order === 'asc' " icon="fas fa-angle-up" />
<font-awesome-icon v-else icon="fas fa-angle-down" />
</span>
</th>
For the Second problem #2 (Not very clear), Better to move these functions to the DataTable and add the actions as a new column, short example:
<td><button #click="editLink(key1)">Edit link or some fa fa icon</button></td>
Add as prop in the DataTable:
props: {
fetchUrl: {type: String, required: true},
columns: {type: Array, required: true},
headers: {type: Object, required: true} //<-- This
},

get a photo preview over v-for loop

I want to have a photo preview in my code below. I'm working with BootstrapVue.
But I don't know where the problem is.. after looking in my Vue devtools I can see that the form-file has the picture, but my "watch" is not working..
It would be very helpful if someone can help me out !
My template:
<template>
<div>
<div v-for="item in files" :key="item.id">
<b-button v-b-toggle="item.id" variant="danger btn-block mb-2">
Upload {{ item.id }}</b-button>
<b-collapse :id="item.id" class="mt-2">
<div class="m-2 mt-3">
<table class="table table-striped mt-2">
<tbody>
<div class="mt-3 mb-2 ml-1">Upload</div>
<b-form-file
:v-model="item.request"
placeholder="Upload ..."
class="mb-2"
></b-form-file>
<b-img
v-if="item.request"
:src="item.src"
class="mb-3"
fluid
block
rounded
></b-img>
</tbody>
</table>
</div>
</b-collapse>
</div>
</div>
</template>
My script:
<script>
const base64Encode = (data) =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(data);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
export default {
data() {
return {
files: [
{ id: "1", src: null, request: null },
{ id: "2", src: null, request: null },
{ id: "3", src: null, request: null },
],
};
},
watch: {
files: {
deep: true, //watch changes at objects properties
handler(newValue, oldValue) {
if (newValue !== oldValue) {
if (newValue) {
base64Encode(newValue.src)
.then((value) => {
newValue.src = value;
})
.catch(() => {
newValue.src = null;
});
} else {
newValue.src = null;
}
}
},
},
},
};
</script>
You made a little mistake!
Instead of writing :v-model for b-form-file component use v-model.
In watch property you cannot understand which upload inputs changed. So try using input event handler for b-form-file. Then you can pass index of each files and run base64Encode function.
You can see working version of your code here.

Vue.js - passing value from parent to child does not work (as expected)

I pass the value from parent template to child template under this scheme:
parentModel -> parentTemplate -> prop -> childModel -> childTemplate.
That is, when getting in a child model, I need to handle value before installing in template... but it doesn't work!
My method is similar to a kludge =(
Parent:
<template>
<section class="login-wrapper border border-light">
<form id="add-form" class="form-signin" enctype="multipart/form-data" #submit.prevent="send">
<label>
<span>Images</span>
<input type="file" id="files" ref="files" multiple #change="addFile()"/>
</label>
<button type="submit">Submit</button>
</form>
<div id="files-container">
<div v-for="(file, index) in files" class="file-listing" v-bind:key="index">
<Preview :msg="file"></Preview><!-- here I send data to the child with :msg property -->
</div>
</div>
</section>
</template>
<script>
import Preview from "../Partial/ImagePreview.vue"
export default {
name: "ProductAdd",
components: {
Preview
},
data() {
return {
files: []
}
},
methods: {
addFile() {
for (let i = 0; i < this.$refs.files.files.length; i++) {
const file = this.$refs.files.files[i]
this.files.push( file );
}
},
async send() {
/// Sending data to API
}
}
}
</script>
Child:
<template>
<section>
<span>{{ setImage(msg) }}</span><!-- This I would like to avoid -->
<img :src="image_url" alt=""/>
</section>
</template>
<script>
export default {
name: 'ImagePreview',
data: () => {
return {
image_url: ""
}
},
props: [ "msg" ],
methods: {
setImage(data) {
const reader = new FileReader();
reader.onload = (event) => {
this.image_url = event.target.result;
};
reader.readAsDataURL(data);
return null;
}
}
}
</script>
I'm so sorry for a stupid question (perhaps), but I rarely work with frontend.
Now there is such a need =)
PS: I tried using "watch" methods, it doesn't work in this case. When changing an array in the parent component, these changes are not passed to child
But its work.. I see selected image preview
const Preview = Vue.component('ImagePreview', {
data: () => {
return {
image_url: ""
}
},
template: `
<section>
<span>{{ setImage(msg) }}</span><!-- This I would like to avoid -->
<img :src="image_url" alt=""/>
</section>
`,
props: [ "msg" ],
methods: {
setImage(data) {
const reader = new FileReader();
reader.onload = (event) => {
this.image_url = event.target.result;
};
reader.readAsDataURL(data);
return null;
}
}
});
new Vue({
name: "ProductAdd",
components: {Preview},
data() {
return {
files: []
}
},
methods: {
addFile() {
for (let i = 0; i < this.$refs.files.files.length; i++) {
const file = this.$refs.files.files[i]
this.files.push( file );
}
},
async send() {
/// Sending data to API
}
}
}).$mount('#container');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='container'>
<section class="login-wrapper border border-light">
<form id="add-form" class="form-signin" enctype="multipart/form-data" #submit.prevent="send">
<label>
<span>Images</span>
<input type="file" id="files" ref="files" multiple #change="addFile()"/>
</label>
<button type="submit">Submit</button>
</form>
<div id="files-container">
<div v-for="(file, index) in files" class="file-listing" v-bind:key="index">
<Preview :msg="file"></Preview><!-- here I send data to the child with :msg property -->
</div>
</div>
</section>
</div>

Vue: Data fields changing as inputs are edited

Editing to remove component names but basically trying to use v-modal to get component data inside.
Editing to remove component names but basically trying to use v-modal to get component data insideEditing to remove component names but basically trying to use v-modal to get component data inside
wp.vue . (Modal component inside template as Child component)
<template>
<div class="cart-component">
<Modal
:modal-type="this.modalType"
:customer="this.customer"
>
<div class="shipping-info">
<span class="title">Shipping Address</span>
<span class="personal-details">{{this.customer.street_address + "," + this.customer.city + "," + this.customer.state}}</span>
<span
data-toggle="modal"
v-on:click="() => {this.modalType = 'EditShipping'}"
data-target="#"
class="edit">Edit
</span>
</div>
</template>
<script>
import Modal from './Modal.vue';
export default {
data() {
return {
cart: [],
cartItems: [],
customer: {},
dataLoaded: false,
modalType: null
}
},
mounted() {
this.getCartItems()
},
components: {
'Modal': Modal
},
methods: {
getCartItems: function() {
axios
.get('/api/new-account/cart')
.then(response => {
this.cart = response.data.cart;
this.cartItems = response.data.cart.items;
this.customer = response.data.customer;
this.dataLoaded = true;
});
}
</script>
S.vue (child of Shop.vue)
<template>
<div class="shipping-input-containers">
<div class="name">
<div class="first-name">
<input v-model="customer.name.split(` `)[0]" class="default-input"></input>
<span class="input-small">First Name</span>
</div>
<div class="last-name">
<input v-model="customer.name.split(` `)[1]" class="default-input"></input>
<span class="input-small">Last Name</span>
</div>
</div>
<div class="street-address">
<input v-model="customer.street_address" class="default-input"></input>
<span class="input-small">Street Address</span>
</div>
<div class="city">
<input v-model="customer.city" class="default-input"></input>
<span class="input-small">City</span>
</div>
<div class="state">
<input v-model="customer.state" class="default-input"></input>
<span class="input-small">State</span>
</div>
<div class="zip-code">
<input v-model="customer.zip" class="default-input"></input>
<span class="input-small">Zip Code</span>
</div>
</div>
<script>
export default {
props: {
customer: {type: Object}
},
methods: {
updateShippingAddress: function() {
axios.post('/api/account/update-address', {
street_address: this.customer.street_address || "",
city: this.customer.city || "",
country_code: this.customer.country_code || "",
state: this.customer.state || "",
zip: this.customer.zip || "",
apartment: this.customer.apartment || "",
phone_number: 221232123,
})
.then(response => {
if(response.data.success) {
this.$parent.getCartItems();
let msg = "Address updated!"
this.showFlashMsg(msg, true)
}
})
.catch(err => {
});
}
}
}
</script>
Since you use same model between Shop and Modal, it works like that.
First of all, you need to use new name such as “customer.newCity” in the Modal.
And then when users update via updateShippingAddress, you can POST the user’s city like this :
//...
city: this.customer.newCiity ? this.customer.newCity : (this.customer.city || "")
//...

How do I delete specific child React components?

JSFiddle: https://jsfiddle.net/morahood/cp569zg6/38/
When I delete a child ServiceItem component from the ServiceList component, I am running into an issue where the last ServiceItem in the list is removed instead of the targeted ServiceItem. The JSFiddle link above will help understand the issue, just make sure you enter in some identifiable information in the input fields so you can see what is getting deleted.
How do I fix my application so that the respective component is deleted?
var ServiceForm = React.createClass({
render: function() {
return (
<form onSubmit={ this.handleFormSubmission } >
{ this.renderServiceItemList() }
<div className="btn btn-default btn-sm" onClick={ this.addServiceItem }>+ Append New Service Item</div>
<button type="submit" className="btn btn-success btn-lg pull-right">Submit</button>
</form>
);
},
getInitialState: function() {
return ({
serviceItems: [this.renderServiceItem]
});
},
handleFormSubmission: function(event) {
event.preventDefault();
var data = this.refs.service_item_list.getAllServiceItems();
var json = {
"service_request" : {
"services" : data,
"additional_recipients" : 'test#example.com',
"comments" : 'Hello world!'
}
};
console.log(json);
},
addServiceItem: function(event) {
var copy = this.state.serviceItems.slice();
copy.push(this.renderServiceItem);
this.setState({
serviceItems: copy
});
},
renderServiceItem: function(item, i) {
return (
<ServiceItem removeServiceItem={ this.removeServiceItem } data={item} key={i} ref={"service_item_" + i} />
);
},
removeServiceItem: function(event) {
var index = parseInt(event.target.value, 10);
var copy = this.state.serviceItems.slice();
copy.splice(index, 1);
this.setState({
serviceItems: copy
});
},
renderServiceItemList: function() {
return (
<ServiceItemList serviceItems={ this.state.serviceItems }
renderServiceItem={ this.renderServiceItem }
removeServiceItem={ this.removeServiceItem }
ref="service_item_list" />
);
}
});
var ServiceItemList = React.createClass({
render: function() {
var content;
if (this.props.serviceItems.length < 1) {
content = <div className="empty-service-list">There are no service items, click on append new service item below!</div>;
} else {
content = this.props.serviceItems.map(this.props.renderServiceItem);
}
return (
<div>
{ content }
</div>
);
},
getAllServiceItems: function() {
var data = [];
for (var ref in this.refs) {
data.push(this.refs[ref].serializeItem());
}
var merged = [].concat.apply([], data);
return(merged);
}
});
var ServiceItem = React.createClass({
render: function() {
return (
<div className="row">
<div className="col-sm-3">
<div className="form-group service-item">
<label>Service Item </label>
<select multiple ref="service_types" className="form-control">
<option>Oil Change</option>
<option>Tire Rotation</option>
<option>New Wiper Blades</option>
</select>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Customer </label>
<select ref="customer" className="form-control">
<option>Troy</option>
<option>Dave</option>
<option>Brandon</option>
</select>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Manufacturer </label>
<div className="input-group">
<input ref="manufacturer" type="text" className="form-control" />
</div>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Model </label>
<div className="input-group">
<input ref="model" type="text" className="form-control" />
</div>
<a href="#" onClick={ this.props.removeServiceItem }>
<span aria-hidden="true" className="remove-fields" onClick={ this.props.removeServiceItem }>×</span>
</a>
</div>
</div>
</div>
);
},
getInitialState: function() {
return({
service_types: [],
customer: '',
manufacturer: '',
model: ''
});
},
serializeItem: function() {
var customer = this.refs.customer.value;
var manufacturer = this.refs.manufacturer.value;
var model = this.refs.model.value;
var selected = this.getSelectedServiceItems();
var services = this.getSelectedServiceItems().map(function(item) {
return (
{
"service" : {
"service_type" : item,
"customer" : customer,
"manufacturer" : manufacturer,
"model" : model
}
}
)
});
return(services);
},
getSelectedServiceItems: function() {
var select = this.refs.service_types;
var values = [].filter.call(select.options, function (o) {
return o.selected;
}).map(function (o) {
return o.value;
});
return(values);
}
});
ReactDOM.render(
<ServiceForm />,
document.getElementById('container')
);
Your issue is with your key={i}.
React lists requires the key of an item to be unique to the item, regardless of the position of the item in the list. This allows react to do smart management of the list in case of updates.
React will NOT render the following update correctly:
From: To:
name: key: name: key:
Bill 0 Bill 0
Charlie 1 Dave 1
Dave 2
Because react thinks that the Charlie record is unchanged (because the key is unchanged).
I would advise you to put some sort of ID from the service-item retrieved as key.
On name, e.g.
From: To:
name: key: name: key:
Bill Bill Bill Bill
Charlie Charlie Dave Dave
Dave Dave
React will render the update correctly in this case (because key is unique to the item).

Categories

Resources