I have this state defined:
constructor(props){
super(props);
this.state = {
open: false,
customers:[],
customer:{},
products:[],
product:{},
orders:[],
order:{},
newForm:true,
phoneNumbererror:null,
shop:this.props.salon,
value:'a',
showTab:'none',
slideIndex: 0,
};
}
With the following function which contains a fetch, I recieve an array of objects with responseData.
getHistory(){
console.log("Log antes del fetch de customer id");
console.log(this.state.customer._id);
fetch(
DOMAIN+'/api/orders/customer/'+this.state.customer._id, {
method: 'get',
dataType: 'json',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization':'Bearer '+this.props.token
}
})
.then((response) =>
{
return response.json();
})
.then((responseData) => {
let orders = responseData.map((order) => {
return order.orderStatusChange ? Object.assign({}, order, {
status: order.orderStatusChange[0].status
}) : order;
});
this.setState({orders:orders});
console.log("Log del responseData");
console.log(responseData);
console.log(responseData.orderStatusChange[0]);
})
.catch(function() {
console.log("error");
});
}
This function is called in handleCellClick, where I pass some data from the consumer, such as the ID:
handleCellClick(y,x,row){
this.setState({
open:true,
slideIndex: 0,
newForm:false,
customer:{...row}
});
this.getProfiles();
this.getHistory();
}
The JSON object obtained from the fetch and kept within this.state.orders looks like this:
(29) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0:
created:"2017-07-06T15:58:07.958Z"
customer:"59561f3f1d178e1966142ad7"
lastModified:"2017-07-06T15:58:07.958Z"
orderList:[]
orderStatusChange:Array(1)
0:{status: "5", comments: "Creado en back antes de pagar", _id: "595e5e0f60fbf65149916b7c", created: "2017-07-06T15:58:07.958Z"}
length:1
__proto__:Array(0)
shop:"59108159bc3fc645704ba508"
totalAmount:4000
__v:0
_id:"595e5e0f60fbf65149916b7b"
__proto__:Object
As shown previously in the fetch, with this line this.setState({orders:responseData}) I can pass orders to the table where I want the id, date, status and price to be displayed:
<DataTables
height={'auto'}
selectable={false}
showRowHover={true}
columns={HISTORY_TABLE_COLUMNS}
data={this.state.orders}
showCheckboxes={false}
rowSizeLabel="Filas por página"
/>
The table called is:
const HISTORY_TABLE_COLUMNS = [
{
key: '_id',
label: 'Número de pedido',
style:{width: '37%'}
}, {
key: 'created',
label: 'Fecha del pedido',
style:{width: '33%'}
}, {
key: 'status',
label: 'Estado',
style:{width: '13%'}
}, {
key: 'totalAmount',
label: 'Total',
style:{width: '17%'}
}
];
How can I format the price (totalAmount) to have 2 decimals and print next to it the € symbol?
CAPTURE FOR BETTER UNDERSTANDING
This solution works fine with node module material-ui-datatables version 0.18.0
You can use render method in column settings to work on the column data.
const currencyToAppend = '€';
const HISTORY_TABLE_COLUMNS = [
{
....
}, {
....
}, {
key: 'totalAmount',
label: 'Total',
style:{width: '17%'}
render: (amount, all) => {
console.log(amount);
console.log(all);
return amount + ' ' + currencyToAppend;
}
}
];
While iterating data in table please do the following.
totalAmount.toFixed(2) + " €"
Update:
I would suggest this change should be done from backend, But any how for now you can handle it in map iterator where you are setting orders like following
const currencyToAppend = ' €';
let orders = responseData.map((order) => {
return order.orderStatusChange ? Object.assign({}, order, {
status: order.orderStatusChange[0].status
},{
totalAmount: order.totalAmount.toFixed(2) + currencyToAppend
}) : Object.assign({}, order, {
totalAmount: order.totalAmount.toFixed(2) + currencyToAppend
});
});
I hope this will solve your problem.
To complement #dev's answer, I'd suggest to have render the cell as a function as that gives you more control
Check out the codesandox demo https://codesandbox.io/s/0VVwq645L
const HISTORY_TABLE_COLUMNS = [
{
key: "_id",
label: "Número de pedido",
style: { width: "37%" },
value: item =>
<code>
{item._id}
</code>
},
{
key: "created",
label: "Fecha del pedido",
style: { width: "33%" },
value: item => <Time value={item.created} />
},
{
key: "status",
label: "Estado",
style: { width: "13%" },
value: item =>
<span>
{item.status}
</span>
},
{
key: "totalAmount",
label: "Total",
style: { width: "17%" },
value: item => <Amount value={item.totalAmount} currency="€"} />
}
];
Related
I'm creating a React-native app, and I'm getting some data with AsyncStorage which I transform into an Array with Objects which will be combined/concatnated with data that I fetch from an API
const available_streams = streams.channels.filter(stream => !stream.is_live)
const the_streams = available_streams.map(available_stream => {
available_stream.checked = false
return available_stream
})
console.log(the_streams) :
(3) [{…}, {…}, {…}]
0: {name: "channel1", url: "wss%3A%2F%2Fwebsite.com", app_name: "streams", server_name: "channel1", is_live: false, …}
1: {name: "channel3", url: "wss%3A%2F%2Fwebsite.com", app_name: "streams", server_name: "channel3", is_live: false, …}
2: {app_name: "sms", url: "website.com:4443", server_name: "93b5d83448", name: "Test", is_live: false, …}
length: 3
let saved_stream_names = []
// LOCAL VALUES
get_session_value('url').then(url => {
get_session_object('stream_names')
.then(stream_names => {
stream_names.forEach(name => {
saved_stream_names.push({
name: name,
checked: false,
is_live: false,
url: url
})
})
})
})
Array.prototype.push.apply(saved_stream_names, the_streams)
console.log(saved_stream_names)
the console.log prints the following:
(3) [{…}, {…}, {…}] <---- !! WHY 3 ?!!
0: {name: "channel1", url: "wss%3A%2F%2F.website.com", app_name: "streams", server_name: "channel1", is_live: false, …}
1: {name: "channel3", url: "wss%3A%2F%2Fwebsite.com", app_name: "streams", server_name: "channel3", is_live: false, …}
2: {app_name: "sms", url: "wss://website3:4443/", server_name: "93b5d83448", name: "Test", is_live: false, …}
3: {name: "Xxx", checked: false, is_live: false, url: "https://website.com/"}
4: {name: "Next", checked: false, is_live: false, url: "website.com"}
5: {name: "Arghhj", checked: false, is_live: false, url: "https://website.com/"}
length: 6
also console.log(saved_stream_names.length) says it's 3 in size
and i cannot loop over the last 3 objects. What kind of wizardry is this?
It is a synchronicity problem.
get_session_value('url')
Returns a promise, the code inside the then() will only execute when this promise is solved.
Even tho console.log(saved_stream_names) is the last line of code, it is being executed before the code inside the then(). Try moving the console.log to inside the then:
let saved_stream_names = []
// LOCAL VALUES
get_session_value('url').then(url => {
get_session_object('stream_names')
.then(stream_names => {
stream_names.forEach(name => {
saved_stream_names.push(
({
name: name,
checked: false,
is_live: false,
url: url
})
)
})
console.log(saved_stream_names)
})
})
Array.prototype.push.apply(saved_stream_names, the_streams)
I believe it might be because console.log gets called before get_session_value() finishes running. So when console.log is printed it only contains the values of the_streams. However since it is referencing an array, it will show the whole thing by the time you look at it. You can confirm this by doing console.log(JSON.stringify(saved_stream_names)).
I would suggest you put these lines of code:
Array.prototype.push.apply(saved_stream_names, the_streams)
console.log(saved_stream_names)
inside the get_session_object.then() function.
I have a login form and where the inputs (email & password) are bound. On clicking the button to submit the form, it prevents the default behaviour and uses the login method defined in the Login.vue; Scripts.
While consoling in Login.vue; Scripts; login method, the form data printed out the {email: 'email', password: 'password'} object (desired). Once it is passed to the action (await this.signIn(this.form)), it consoled out a Vue component all of the sudden. I don't understand why this happened and how can this be solved?
Login.vue Component
Form
<form #submit.prevent="login" method="POST">
<input
type="text"
v-model="form.email"
/>
<input
type="password"
v-model="form.password"
/>
<button class="btn btn-primary">Login</button>
</form>
Scripts
<script>
import { mapActions } from 'vuex'
export default {
data() {
return {
form: {
email: '',
password: '',
},
}
},
computed: {
...mapActions('auth', ['signIn']),
},
methods: {
async login() {
/***************************************
* *
* Print out the form data object *
* *
****************************************/
console.log(this.form)
await this.signIn(this.form)
},
},
}
</script>
Vuex - Auth Module
export const actions = {
signIn({ dispatch, commit }, form) {
/***************************************************************
* *
* Print out a Vue component instead of the passed object *
* *
****************************************************************/
console.log(form)
Auth.signInWithEmailAndPassword(form.email, form.password)
.then(user => {
commit('SET_AUTHENTICATED', true)
commit('SET_USER', user.user)
this.$router.push('/')
})
.catch(err => {
console.log(err)
})
},
}
Console logged content
VueComponent {_uid: 4, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
$attrs: (...)
$children: []
$createElement: ƒ (a, b, c, d)
$el: div
$listeners: (...)
$options: {parent: VueComponent, _parentVnode: VNode, propsData: undefined, _parentListeners: undefined, _renderChildren: undefined, …}
$parent: VueComponent {_uid: 3, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
$refs: {}
$root: Vue {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
$scopedSlots: {$stable: true, $key: undefined, $hasNormal: false}
$slots: {}
$store: Store {_committing: false, _actions: {…}, _actionSubscribers: Array(1), _mutations: {…}, _wrappedGetters: {…}, …}
$vnode: VNode {tag: "vue-component-4", data: {…}, children: undefined, text: undefined, elm: div, …}
form: (...)
login: ƒ ()
signIn: (...)
__VUE_DEVTOOLS_UID__: "1:4"
_c: ƒ (a, b, c, d)
_computedWatchers: {signIn: Watcher}
_data: {__ob__: Observer}
_directInactive: false
_events: {hook:beforeDestroy: Array(1)}
_hasHookEvent: true
_inactive: null
_isBeingDestroyed: false
_isDestroyed: false
_isMounted: true
_isVue: true
_renderProxy: Proxy {_uid: 4, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
_routerRoot: Vue {_uid: 2, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …}
_self: VueComponent {_uid: 4, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
_staticTrees: null
_uid: 4
_vnode: VNode {tag: "div", data: undefined, children: Array(2), text: undefined, elm: div, …}
_watcher: Watcher {vm: VueComponent, deep: false, user: false, lazy: false, sync: false, …}
_watchers: (2) [Watcher, Watcher]
$data: (...)
$isServer: (...)
$props: (...)
$route: (...)
$router: (...)
$ssrContext: (...)
get $attrs: ƒ reactiveGetter()
set $attrs: ƒ reactiveSetter(newVal)
get $listeners: ƒ reactiveGetter()
set $listeners: ƒ reactiveSetter(newVal)
get form: ƒ proxyGetter()
set form: ƒ proxySetter(val)
__proto__: Vue
As Sumurai8 mentioned, I only need to put the ...mapActions('auth', ['signIn']) in methods and not in computed.
methods: {
...mapActions('auth', ['signIn']),
async login() {
console.log(this.form)
await this.signIn(this.form)
},
},
I have an array of Categories that I filter when a user click on a button that selects a specific categories to view. however after maping the categories array, the categories display the desired result in the console however it seems to get lost somehow and the categories dont update in the DOM?
ngOnInit() {
this.initCategories();
this.shopService.filterCategories.subscribe(
(fCategory: string) => {
const filteredCategories = this.categories.filter(category => {
return category.name !== fCategory;
});
for (const obj of filteredCategories) {
obj.checked = false;
}
const newCategories = [];
this.categories.map(obj => {
filteredCategories.filter(fCat => obj);
newCategories.push(obj);
});
this.categories = newCategories;
console.log(this.categories)
}
);
}
initCategories(){
this.categories = [
{name: 'dress', checked: true, displayName: 'Dresses'},
{name: 'top', checked: true, displayName: 'Shirts'},
{name: 'skirt', checked: true, displayName: 'Skirts/Pants'},
{name: 'purse', checked: true, displayName: 'Purse'},
{name: 'bag', checked: true, displayName: 'Bags'},
];
}
result
[{…}, {…}, {…}, {…}, {…}]
0: {name: "dress", checked: true, displayName: "Dresses"}
1: {name: "top", checked: false, displayName: "Shirts"}
2: {name: "skirt", checked: false, displayName: "Skirts/Pants"}
3: {name: "purse", checked: false, displayName: "Purse"}
4: {name: "bag", checked: false, displayName: "Bags"}
however when I log the categories array in ngAfterViewInit
I get this.
[{…}, {…}, {…}, {…}, {…}]
0: {name: "dress", checked: true, displayName: "Dresses"}
1: {name: "top", checked: true, displayName: "Shirts"}
2: {name: "skirt", checked: true, displayName: "Skirts/Pants"}
3: {name: "purse", checked: true, displayName: "Purse"}
4: {name: "bag", checked: true, displayName: "Bags"}
what I tried
this.shopService.filterCategories.subscribe(
(fCategory: string) => {
const filteredCategories = this.categories.filter(category => {
return category.name !== fCategory;
});
for (const obj of filteredCategories) {
obj.checked = false;
}
let newCategories;
newCategories = [...this.categories.map(obj => {
filteredCategories.filter(fCat => obj);
})];
this.categories = newCategories;
console.log(this.categories)
}
);
}
I think you need to play with
this.categories.map(obj => {
filteredCategories.filter(fCat => obj);
both of them return a new array, they don't touch current one.
Therefore I would assume that filteredCategories.filter at least should be assigned somewhere.
// an empty array
const newCategories = [];
// starting a loop, forEach would fit here better because it doesn't return anything.
this.categories.map(obj => {
// the result of this filter won't be assigned anywhere.
filteredCategories.filter(fCat => obj);
// pushing obj to newCategories for every iteration.
// may be you need to wrap it with `if` based on filter result.
newCategories.push(obj);
});
// newCategories is an array with the same items as this.categories.
// because we simply iterate without any conditions.
console.log(newCategories);
In the update part of your question filter still doesn't do anything.
Its result should be assigned or used in a condition.
newCategories = [...this.categories.map(obj => {
filteredCategories.filter(fCat => obj); // <- should be assigned
})];
if you want to add only filtered only active category.
ngOnInit() {
this.initCategories();
this.shopService.filterCategories.subscribe(
(fCategory: string) => {
const filteredCategories: FilterBarComponent['categories'] = [];
for (const category of this.categories) {
filteredCategories.push({
...category,
checked: category.name === fCategory,
});
}
this.categories = filteredCategories;
this.updateCategories();
}
);
}
I have initial state as
sites = [
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
{id, name, vehicles[], drivers[]},
];
I'm trying to add a vehicle to a given site when selected from a list which is in a component SiteVehcleSelection and the method that handles the selection is:
handleVehicleSelection = (event) => {
const vehicle = this.props.vehicles.find((v) => v.id === parseInt(event.target.dataset.id, 10));
this.props.handleVehicleSelection(event, this.state.site.id, {...vehicle});
};
which passes it up to parent SiteList method:
handleVehicleSelection = (event, siteId, vehicle) => {
this.props.dispatch(siteActions.handleVehicleSelect(siteId, vehicle), event.target.checked);
}
called from the SiteList class:
export function handleVehicleSelect(siteId, vehicle, cmd){
return (dispatch) => {
debugger;
return fetch(`${BASE_URL}/accounts/site-vehicle-action/${siteId}/${vehicle.id}/${cmd}`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: ''
}).then((res) => {
return res.json();
}).then((json) => {
if (json.msg === true) {
dispatch(vehicleSelect(siteId, vehicle));
}
});
}
}
which dispatches to this:
export function vehicleSelect(siteId, vehicle){
return {type: actionTypes.ADD_VEHICLE_TO_SITE, siteId, vehicle};
}
and my reducer is:
case actionTypes.ADD_VEHICLE_TO_SITE:
debugger;
const siteIndex = state.findIndex((site) => site.id === action.siteId);
console.log(state);
const newState = [...state.slice(0, siteIndex), {...state[siteIndex], vehicles: [...state[siteIndex].vehicles, action.vehicle],}, ...state.slice(siteIndex +1)];
console.log(newState);
return newState;
when I log before and after the changes have taken place, the vehicle has been added to the correct site but it does not show/refresh in the view here is the logging of the state before and after.
Before change :
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(0)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
After change:
0: {drivers: Array(0), id: 1, name: "Site One", vehicles: Array(1)}
1: {drivers: Array(0), id: 2, name: "Site Two", vehicles: Array(0)}
2: {drivers: Array(0), id: 3, name: "Site Three", vehicles: Array(0)}
length: 3
__proto__: Array(0)
can see the first one had the vehicle added correctly and this is the new state but nothing happens on return as if sitesList does not refresh.
Hope this edit helps explain more.
I think below code will shed some light. I assume you have the corresponding indexes.
let state = [
{ id: "id1", name: "name1", items: [{},{}] },
{ id: "id2", name: "name2", items: [{},{}] },
]
function reducer() {
switch("Action") {
case ADD_SITE: { // add new element to state
return [
...state,
payload.site,
]
}
case ADD_SITE_AT_INDEX: { // add new element to state at index: idx
return [
...state.slice(0, idx),
payload.newSite,
...state.slice(idx)
]
}
case ADD_ITEM: { // add new item to site with index: idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items,
payload.newItem
],
},
...state.slice(idx+1)
]
}
case ADD_ITEM_AT_INDEX: { // add new item to site with index: idx, at item index: item_idx
return [
...state.slice(0, idx),
{
...state[idx],
items: [
...state[idx].items.slice(0, item_idx),
payload.newItem,
...state[idx].items.slice(item_idx),
],
},
...state.slice(idx+1)
]
}
}
}
let say this is your structure of state with keys (i am giving random name to the keys so i can explain)
{
data:[{id:"some_id",name:"some_name",items1:[{},{}]}]
}
//suppose this is your reducer code.
case ADD_ITEM:
return{
...state,
data: state.data.map(val=>{
if(val.Id===action.payload.Id){
return{
...val,
items1:[...val.items1,action.payload.data]
}
}
return{...val}
})
}
here you'll be sending Id and data from the action like:
{type:"ADD_ITEM",payload:{Id,data}}
where id will be the id of first level object which's array needs to be updated,
and data will be the data you want to add into the array..
If you just want to add an object to an array with a given structure, you can do this:
Structure
[
{ id, name, items1[{object},{object}]
]
copy an existing state and then add a new object to the end of the array.
return {
...state,
items1: state.items1.concat(action.newObject),
};
or with ES6 spread
return {
...state,
items1: [...state.items1, action.newObject],
};
// state: { id, name, items1: [{object},{object},{newObject}] }
We got a ReactJS frontend delivered for our school project. We have to make a Laravel backend for it. I'm using an API to fetch the dashboard layout from the database. The current frontend makes use of this variable:
const originalLayouts = getFromLS("layouts") || [];
To set the state from the local storage with this function:
function getFromLS(key) {
let ls = {};
if (global.localStorage) {
try {
ls = JSON.parse(global.localStorage.getItem("rgl-8")) || {};
} catch (e) {
/*Ignore*/
}
}
return ls[key];
}
Where the states are set:
this.state = {
items: originalLayouts.map(function(i, key, list) {
return {
i: originalLayouts[key].i,
x: originalLayouts[key].x,
y: originalLayouts[key].y,
w: originalLayouts[key].w,
h: originalLayouts[key].h,
widget: originalLayouts[key].widget,
minW: originalLayouts[key].minW,
minH: originalLayouts[key].minH,
maxH: originalLayouts[key].maxH
};
}),
selectedOption: '',
newCounter: originalLayouts.length
};
To fetch the data from the database and put the data into the items state I made this function:
loadData = () => {
let dashboardId = 1;
return axios
.get('api/dashboards/' + dashboardId)
.then(result => {
console.log(result);
this.setState({
originalLayouts: result.data,
selectedOption: '',
newCounter: originalLayouts.length
});
console.log(result.data);
})
.catch(error => {
console.error('error: ', error);
})
};
And I call this function in componentDidMount:
componentDidMount() {
this.loadData();
}
When I console log result it shows me this:
data: Array(2), status: 200, statusText: "OK", headers: {…}, config: {…}, …}
config: {adapter: ƒ, transformRequest: {…}, transformResponse: {…}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", …}
data: (2) [{…}, {…}]
headers: {date: "Tue, 23 Oct 2018 08:18:41 +0000, Tue, 23 Oct 2018 08:18:41 GMT", host: "127.0.0.1:8000", x-powered-by: "PHP/7.2.3", x-ratelimit-remaining: "58", content-type: "application/json", …}
request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
status: 200
statusText: "OK"
__proto__: Object
And when I console log result.data I get:
(2) [{…}, {…}]
0: {id: 1, dashboardId: 1, w: 2, h: 5, x: 0, …}
1: {id: 2, dashboardId: 1, w: 2, h: 1, x: 0, …}
length: 2
__proto__: Array(0)
Why is originalLayouts not set with the data from the arrays? Is this because I also have a dashboardId and id in my arrays? I also thought it could be something with setting the states because it makes use of the originalLayouts veriable. Or am I still missing something in my function? I'm not very experienced with React so any help is useful.
Update:
I changed:
this.setState({
originalLayouts: result.data,
selectedOption: '',
newCounter: originalLayouts.length
});
to:
this.setState({
items: result.data,
selectedOption: '',
newCounter: originalLayouts.length
});
This gives me this error:
Uncaught Error: ReactGridLayout: ReactGridLayout.children[0].static must be a boolean!
So that probably means I'm not setting the properties properly now.
Update 2:
In my database the properties moved and static were saved as 0 instead of false. So I changed those properties to false but I still got the same error:
ReactGridLayout: ReactGridLayout.children[0].static must be a boolean!
In your loadData(), you are setting the state of "originalLayouts" but your key in your initial state is "items". Have you tried to do this ?
this.setState({
items: result.data, // Here put items instead of originalLayouts
selectedOption: '',
newCounter: originalLayouts.length
});
Then you can call this.state.items to get your result.data