Vue getter returns undefined when page reload - javascript

I have a blog with some posts. When you click on the preview you will redirect on the page post.
On the page of the post, I use a getter to load the correct post (I use the find function to return object.name which corresponds to the correct object in the array of objects).
const state = {
ricettario: [], // data that contains all recipes (array of objects)
}
const actions = {
// Bind State and Firestore collection
init: firestoreAction(({ bindFirestoreRef }) => {
bindFirestoreRef('ricettario', db.collection('____').orderBy('data'))
})
const getters = {
caricaRicetta(state) {
console.log('Vuex Getter FIRED => ', state.ricettario)
return nameParamByComponent => state.ricettario.find(ricetta => {
return ricetta.name === nameParamByComponent
})
}
}
In the component, I call the getter in the computed property
computed: {
...mapGetters('ricettaStore', ['caricaRicetta']),
ricetta() {
return this.caricaRicetta(this.slug) // this.slug is the prop of the URL (by Router)
}
}
Anything goes in the right way but when I reload the page in the POST PAGE, the getter will fire 2 times:
1. return an error because the state is null
2. return the correct object
// screen below
So everything works fine from the front but not at all in the console and in the App.
I think the correct way is to call the getters in the created hook. What I've to change? It is a problem with the computed prop, getters or state?
POST PAGE:
<template>
<div v-if="ricetta.validate === true" id="sezione-ricetta">
<div class="container">
<div class="row">
<div class="col s12 m10 offset-m1 l8 offset-l2">
<img
class="img-fluid"
:src="ricetta.img"
:alt="'Ricetta ' + ricetta.titolo"
:title="ricetta.titolo"
/>
</div>
</div>
</div>
</div>
<div v-else>
...
</div>
</template>

You are trying to validate undifined property. So you need to check ricetta first.
Try like this:
<div v-if="ricetta && ricetta.validate === true" id="sezione-ricetta">

Database synchronization is asynchronous, ricettario is initially an empty array. Computed value is recomputed once synchronization is finished and ricettario array is filled, the component is updated.
Even if ricettario weren't empty, find may return undefined if it finds nothing. This needs to be handled where ricetta is used:
<div v-if="ricetta && ricetta.validate" id="sezione-ricetta">

The error log is quite explicit, there is a xxx.validate somewhere in your Ricetta component template, but that xxx is undefined.
Because of this, your app crashes and stops working. I doubt it has anything to do with Vuex

Related

Saving Values to Backend from TextBoxes using React Flux Pattern

I have several text boxes and a save button
Each text box value is loaded using the following approach
{
this.getElement('test3lowerrangethreshold', 'iaSampling.iaGlobalConfiguration.test3lowerrangethreshold',
enums.IASamplingGlobalParameters.ModerationTest3LowerThreshold)
}
private getElement(elementid: string, label: string, globalparameter: enums.IASamplingGlobalParameters): JSX.Element {
let globalParameterElement =
<div className='row setting-field-row' id={elementid}><
span className='label'>{localeHelper.translate(label)}</span>
<div className="input-wrapper small">
<input className='input-field' placeholder='text' value={this.globalparameterhelper.getDataCellContent(globalparameter, this.state.globalParameterData)} />
</div>
</div>;
return globalParameterElement;
}
Helper Class
class IAGlobalParametesrHelper {
public getDataCellContent = (globalparameter: enums.IASamplingGlobalParameters, configdata: Immutable.List<ConfigurationConstant>) => {
return configdata?.find(x => x.key === globalparameter)?.value;
}
}
This works fine. Now the user is allowed to update these text values.And on click of save the changes should be reflected by calling a web api .
I have added an onlick event like this
<a href='#' className='button primary default-size' onClick={this.saveGlobalParameterData}>Save</a>
Now inorder to save the data i need a way to identify the text element which has changed.For that i have added an update method within the Helper class
public updateCellValue = (globalparameter: enums.IASamplingGlobalParameters, configdata: Immutable.List<ConfigurationConstant>,updatedvalue:string) => {
let itemIndex = configdata.findIndex(x => x.key === globalparameter);
configdata[itemIndex] = updatedvalue;
return configdata;
}
and return the updated configdata ,and i plan to call this method in the onchange event of every text box like this
<input className='input-field' placeholder='text' onchange={this.setState({ globalParameterData: this.globalparameterhelper.updateCellValue(globalparameter, this.state.globalParameterData, (document.getElementById(elementid) as HTMLInputElement).value})}
But this does not seem like a correct approach as there are number of syntactical errors. I initially got the data using an actioncreator like this.Please advice.
samplingModerationActionCreator.getGlobalParameters();
samplingModerationStore.instance.addListener(samplingModerationStore.SamplingModerationStore
.IA_GLOBAL_PARAMETER_DATA_GET_EVENT,
this.getGlobalParameterData);
}

Vuejs: watching state variable from component

I have a global variable that is populated with an API call when a component is mounted.
I also have a chart component that I would like to show if that variable is not null (i.e. has the request has finished and it has been populated).
At the moment to render the chart I am using this:
<template>
<div class="container">
<b-chart
v-if="$store.state.lists[api_key] != null"
:chartdata="$store.state.lists[api_key]"
:options="options"
/>
</div>
</template>
I have tried moving this check $store.state.lists[api_key] != null to computed or watch, to minimise the inline scripting, but I can't seem to get it to work. Would someone please show me how.
Try this:
computed: {
canShowChart() {
return this.$store.state.lists[this.api_key] != null;
}
}
<b-chart
v-if="canShowChart"
:chartdata="$store.state.lists[api_key]"
:options="options"
/>
Since null values are interpreted as "falsy", and assuming you have an "api_key" data variable, you can use it this way:
computed: {
chartData() {
return this.$store.state.lists[this.api_key]
}
}
<template>
<div class="container">
<b-chart
v-if="chartData"
:chartdata="chartData"
:options="options"
/>
</div>
</template>

Proper way of object copy from one element to another in Vue.js

I am new to Vue.js (I mostly use PHP) and I am trying to creating simple view where user can add an object from one component and place it's copy into another component.
Main template
<template>
<div class="left">
<TaskList :tasks="tasks" v-on:pinned-add-task="pinnedAddTask" />
</div>
<div class="right">
<PinnedList :pinned="pinned" />
</div>
</template>
TaskList
<template>
<div class="task-list">
<div v-for="task in tasks" :key="task.id">
<TaskItem :task="task" v-on:pinned-add-task="$emit('pinned-add-task', task)" />
</div>
</div>
</template>
TaskItem
<template>
<div>
<p>{{task.name}}</p>
<button v-on:click="$emit('pinned-add-task', task)">+</button>
</div>
</template>
And as far as I am aware object "task" is passed by reference and when I try to create an empty object or an array and insert "task" into that newly created object/array when I change original "task" it is also being changed inside that new object and I don't want that.
I am getting my data (tasks) from API that I have created and I am using pagination system so I want to be able to switch pages without losing it from the pinned page.
I created code which looks like this but I don't like it and I don't think that's a good way to do this:
pinnedAddTask(item) {
let pQuantity = 1; // I use this value because I want to be able to pin one task multipletimes
let left = this.pinned;
let right = [];
for (let task of this.pinned) {
if(item.id == task.id) {
pQuantity = task.quantity + 1;
left = this.pinned.filter(eItem => eItem.id < item.id);
right = this.pinned.filter(eItem => eItem.id > item.id);
}
}
const clone = {...item, quantity: pQuantity};
this.pinned = [...left, clone, ...right];
}
Can anyone confirm or reject this?
Yes this one is fine if thats just a shallow copy [ level-one object].
But if you are having a nested object then you might have to use recursive methodology or use any external libary like lodash

React Child Component Loop not redenring

Well I have one of the views from my single page application that is a Quiz, But when a click to generate the Components through a loop based on information on my array state he doesn't render it. I'm using react-router in the index.js maybe this information can help.
Gabarito.js
return(
<div>
<h1 className="display-1">Resposta-#{this.props.chave}-
{this.props.alternativa}</h1>
</div>);
Quiz
state = {
respostas:[10],
gabarito:['Verdadeiro','Falso','Falso','Verdadeiro','Verdadeiro','Falso','Verdadeiro','Falso','Verdadeiro','Verdadeiro'],
correcao:[],
novogabarito: this.eachcomponent
}
alterarevento = (evento,index) =>{
let array = this.state.respostas;
array[index] = evento.target.value;
this.setState({respostas:array});
console.log(this.state.respostas[index])
}
gerargabarito = () =>{
for(let n=0;n<10;n++){
if(this.state.respostas[n]===this.state.gabarito[n]){
this.state.correcao.push('Certa');
}
else{
this.state.correcao.push('Errada');
}
}
console.log(this.state.correcao);
}
eachcomponent = () =>{
return(this.state.correcao.map(resposta=><Gabarito chave={this.state.correcao.indexOf(resposta)} alternativa={resposta}/>));
}
Render of function
<div className="row justify-content-center">
<span id="teste">{this.state.novogabarito}</span>
</div>
</div>
);
}
}
Perhaps I am overlooking something...but it does not look like you are ever invoking your alterarevento and gerargabarito functions. So when you call your eachcomponent your correcao array in your state is still empty so you are not mapping anything.
Before your return statement in your eachcomponent function, try logging correcao to the console to see if it is empty.
A word of caution: you should never manipulate your state directly. So, your this.state.correcao.push('Certa');
line should be:
this.setState({ correcao: [...this.state.correcao, 'Certa'] });

Ember Js computed property in component fails when observing object property in service

Have an interesting problem in Ember js.
Here below is the component advanced-search/component.js with two computed properties roomType & room dependent on the service services/advanced-search queryObj.{roomId,roomTypeId} object properties.
The roomType computed property fires and updates the template correctly when an option is selected from the template. However, interestingly, the room computed property fails to fire when a room is selected from the template. This I verified by putting a console.log('checking room computed fired') inside the room computed property.
To explore this anomaly further, I did the following:
I uncommented the code you see in the init method that sets the rooms array which populates the room list dropdown and commented the code in the actions hash inside the setRoomType action method that was initially setting the rooms array. After these changes, the room computed property fires correctly and updates the template.
I noticed the array returned by the this.get('store).findAll('roomType') resulted in the roomType computed property to fire correctly and update the template, so I attempted to change the call for rooms inside setRoomType from roomType.get('rooms') to this.get('store') to see if it also resulted in the room computed property to fire correctly but it still did NOT fire the room computed property. So, I concluded that both the roomType and room computed properties could only fire and update the template correctly if their dropdown list arrays were set in the component's init method.
advanced-search/component.js
export default Ember.Component.extend({
advancedSearch: Ember.inject.service('advanced-search'),
queryObj: Ember.computed.alias('advancedSearch.queryObj'),
init() {
this._super(...arguments);
// I believe because the 'roomTypes` array is set in this init method, the 'roomType' computed property fires correctly and updates the template
this.get('store').findAll('roomtype').then((roomTypes) => {
this.set('roomTypes', roomTypes);
});
// When the 'rooms' array is also initialized here, the 'room' computed property fires successfully on 'room' selection from the dropdown and updates the template
// this.get('store').findAll('room').then((rooms) => {
// this.set('rooms', rooms);
// });
},
roomTypes: [],
roomType: Ember.computed('queryObj.{roomTypeId}', function() {
var that = this;
return this.get('roomTypes').find(function(roomType) {
return that.get('queryObj').roomTypeId === roomType.id;
});
}),
rooms: [],
room: Ember.computed('queryObj.{roomId}', function() {
console.log('checking room computed fired')
var that = this;
return this.get('rooms').find(function(room) {
return that.get('queryObj').roomId === room.id;
});
}),
actions: {
setRoomType(roomType) {
this.get('advancedSearch').setRoomType(roomType);
this.set('room', null);
if (roomType) {
// When rooms array initialized from here the room computed property fails to fire on room selection from drop down
roomType.get('rooms').then((rooms) => {
this.set('rooms', rooms);
});
} else {
this.set('rooms', null);
}
},
setRoom(room) {
this.get('advancedSearch').setRoom(room);
}
}});
Here below is the service code:
services/advanced-search
export default Ember.Service.extend({
queryObj: {},
setRoomType(roomType) {
this.set('queryObj.roomTypeId', roomType.id);
},
setRoom(room) {
this.set('queryObj.roomId', room.id);
}});
Here below is the component template:
advanced-search/template.hbs
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3>Advanced Search</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-2">
{{#power-select placeholder="Room type" allowClear=true selected=roomType options=roomTypes onchange=(action "setRoomType") as |roomType| }} {{roomType.name}} {{/power-select}}
</div>
<div class="col-md-2">
{{#power-select placeholder="Room No" allowClear=true selected=room options=rooms onchange=(action "setRoom") as |room| }} {{room.name}} {{/power-select}}
</div>
</div>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
NB: My desire is for the 'rooms' property to be set from the action method 'setRoomType' through the 'roomType' model's relationship property called 'rooms' so that the rooms are always filtered according to the 'roomType' selected.
Your help will be greatly appreciated.
Ember Version:
Ember Inspector 2.0.4
Ember 2.8.2
Ember Data 2.9.0
jQuery 3.1.1
Ember Simple Auth 1.1.0
You should not set the computed properties like this.set('room', null); This line of code is the culprit i guess.Once you set computed properties like this then this computed property will not fire again.
What you might need is room computed property with get and set. refer setting computed properties guide
test:Ember.computed('dependantKey',{
get(key){
return 'result';
},
set(key,value){
return value;
}
})

Categories