Rendering properties of class objects in VueJS - javascript

today I came across a problem. I have element in template which is set up like this:
<div v-if="object">{{ object.property }}</div>
It was constantly throwing an error which said
cannot read properties of undefined reading 'property'
I was so confused why there is an error like this when there is the v-if and in console it read properly. After that I realised that the object is an instance of class I've defined, it's not a normal js object. Can someone explain why Vue can't read properties of a class instance ?

Most data properties in Vue have a value that will loosely equate to true unless it is a primitive value such as a number or a Boolean.
In your case what I resort to using is v-if="'property' in object", this works as long as the value of object never starts as or becomes null, An initial value of an empty object is required.
export default {
data: () => ({
object: {}
}),
beforeMount () {
console.log('property' in this.object) // Logs false
},
mounted () {
this.object = {
property: 'some value'
}
console.log('property' in this.object) // Logs true
}
}

Related

How to access mapState property inside the method

How can I access the count property inside the method when I use vuex? Please see my code below.
Code screenshot:
Error
[Vue warn]: Computed property "count" was assigned to but it has no setter.
You can access computed properties just like you access your data properties in a component. But since you are mapping the state in this case. You should not increment or alter its value directly inside the component. Instead, you should dispatch an action with the updated/incremented value and use mutation to mutate the value of the count property in the state object.
More detail https://vuex.vuejs.org/guide/mutations.html#commit-with-payload
The mapState you wrote is inside a computed block. By default computed values are read-only, but you can make them read/write by giving them a setter (ie a function to call when modifying the computed value):
computed: {
count: {
get() { return this.$store.state.count; },
set(newValue) {
// This will most likely throw a warning, as it is bad practise.
this.$store.state.count = newValue;
}
}
}
https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter

Vue using v-for to render computed properties after loaded

I'm using v-for to iterate over a computed property, and that computed property depends on a data attribute, which is initiated as null. I will load it in beforeMount.
here is the pseudo-code:
<th v-for="item in computed_list">
{{ item.name }}
</th>
<script>
export default {
name: 'test',
data () {
return {
whole_list: null
}
},
beforeMount () {
this.load()
},
computed: {
computed_list: function() {
if (!this.series) return []
return this.whole_list.slice(1,3)
}
},
methods: {
async load () {
let res = await some_api_call()
this.whole_list = res['data']
}
}
}
</script>
But somehow it failed to render the list, and report TypeError: Cannot read property 'name' of null.
I'm new to Vue and not very familiar with its lifecycle. The basic idea is to render list of data, but those data are loaded somehow after the Vue instance is created. Not sure if it's the correct way to do this.
Initializing a data item to null breaks the VueJS state watching functionality so it won't know about changes to it. Initialize it as an empty object or array instead.
https://012.vuejs.org/guide/best-practices.html
The reason for this is that Vue observes data changes by recursively walking the data object and converting existing properties into reactive getters and setters using Object.defineProperty. If a property is not present when the instance is created, Vue will not be able to track it.
You don’t have to set every single nested property in your data though. It is ok to initialize a field as an empty object, and set it to a new object with nested structures later, because Vue will be able to walk the nested properties of this new object and observe them.

How can I make an Ember computed property depend on all descendent properties of a variable?

I'm trying to create a computed property that I want to be reevaluated whenever any value in a deeply nested object changes. I understand that myObj.[] can be used to reevaluate computed properties whenever any object in an array changes, but I want this to be recursive.
eg I have
// should recalculate whenever myObj.x.y.z changes, or when myObj.a.b.c changes
computed('myObj', function() {
// ...
})
I don't know in advance exactly how the object is structured, and it may be arbitrarily deep.
Neither computed('myObj.[]', ...) nor computed('myObj.#each', ...) seem to work for this.
Any ideas how to do this?
In Ember it is possible to define computed properties at runtime
import { defineProperty, computed } from '#ember/object';
// define a computed property
defineProperty(myObj, 'nameOfComputed', computed('firstName', 'lastName', function() {
return this.firstName+' '+this.lastName;
}));
So taking that a step further, you could dynamically create whatever computed property key string you want at runtime (this could be in component's init() or something):
// define a computed property
let object = {
foo: 'foo',
bar: 'bar'
}
this.set('myObj', object);
let keys = Object.keys(object).map((key) => {
return `myObj.${key}`
});
defineProperty(this, 'someComputed', computed.apply(this, [...keys, function() {
// do something here
}]));
It's up to you to figure out how to properly recursively traverse your objects for all the dependent keys without creating cycles or accessing prototype keys you don't want...or to consider whether or not this is even that good of an idea. Alternatively, you could try to handle the setting of these properties in such a way that retriggers a computation (which would be more in line with DDAU). I can only speculate from what you've provided what works but it's certainly possible to do what you want. See this twiddle in action
could you try anyone computed/obeserver like below..
But try to prefer the computed.
import { observer } from '#ember/object';
import EmberObject, { computed } from '#ember/object';
partOfNameChanged1: observer('myObj','myObj.[]','myObj.#each', function() {
return 'myObj is changed by obeserver';
})
partOfNameChanged2: computed ('myObj','myObj.[]','myObj.#each', function() {
return 'myObj is changed by computed';
})
then in your handlebar/template file
{{log 'partOfNameChanged1 is occured' partOfNameChanged1}}
{{log 'partOfNameChanged2 is occured' partOfNameChanged2}}
Then you have to associate/assign this partOfNameChanged1 / partOfNameChanged2 to some where in the handlebar or to any other variable in your .js file.
As long as you have not assigned this computed/observer property partOfNameChanged1 /partOfNameChanged2 to somewhere, then you will not get it's value.

An array with objects, within an object is undefined

I have an array that contains objects, inside an object.
I can console.log the first object and the array, but when i try to access the objects within the array or use the map-function on the array i get an error that says "Can't read property of undefined".
I have thoroughly searched SO and other sites for similar problems and found some but no answers seems to work for me.
The object looks like this:
{
answers: [{…}],
createdAt: "2019-01-23T10:50:06.513Z",
nested: {kebab: "jjjj", sås: 2, sallad: "kkk"},
text: "weaxcc",
/* etc... */
}
And i can access it using: this.state.data
I want to access objects inside the answers-array like:
this.state.data.answers[0].text
or even :
this.state.data.answers.map().....
But that gives me 'Cannot read property '0' of undefined. The answers-array is not empty.
Any help is appreciated!
EDIT
This is how the objects ends up in my state.
getQuestionFromDb = () => {
axios.get(`http://localhost:3000/questions/${this.state.id}`)
.then(res => this.setState({
data: res.data
}));
};
This function is called in the ComponentDidMount()-method.
Here is my render function (the console.log is causing the error):
render() {
return (
<div className="main-content">
<h2>{this.state.data.text} </h2>
{console.log(this.state.data.answers[0].text)}
<p>Introducing <strong>{this.state.id}</strong>, a teacher who loves teaching courses about <strong>{this.state.id}</strong>!</p>
<input
type="text"
onChange={e => this.setState({ message: e.target.value })}>
</input>
<button onClick={() => {this.handleAnswerPost(this.state.message)}}>Answer</button>
</div>
);
}
}
componentDidMount is getting called when your component becomes part of the DOM but the call you do to populate your state is async due to XHR, which may take 100-300ms more to get the data in your component, so this.state.data.answers won't be available in the initial render() cycle.
since you mentioned using a loop, I suggest setting an initial state shape like
this.state = {
data: {
answers: []
}
}
your initial render won't have anything to loop but as soon as it resolves the data and sets the new state, it will render correctly.
alternatively you can
return this.state.data.answers.length ? loopItemsHere : <div>Loading..</div>
obviously, loopItemsHere can be anything you write to show the answers.
This might not be working when data doesnt contain answers[] at the very first mount for a component.
You may wanna check for your array's existance as following:
const { data } = this.state;
data.hasOwnProperty('answers') && console.log(data.answers[0]);
The reason we can't access object key in Javascript, usually it is because the variable/value is not the type of Object. Please ensure that this.state.data type is Object. You can try to console.log( typeof this.state.data ). We should expect that it is output object. If the type is string, you should parse it first with JSON.parse().
There may be a number of reasons that could cause the data object not to be shown:
The object was not in state at the time of it being called. This may be caused by an async operator not loading in the data. E.g. if you are requesting for the object from the database, chances are that at the time of making a call to retrieve the data (this.state.data), the response had not been given.
The object may not have been parsed into string. Try running console.log(typeof this.state.data). If the output is string, then you may have to parse it. If the output is undefined, then point one is valid

Map Vuex getter to a computed property AFTER component is created

Is it even possible?
I know I can do it like this:
computed: {
...mapGetters({
xyz: 'xyz'
}),
but I was wondering if I can do it using inherited mapGetters, i.e. in created hook:
created () {
this.$options.computed = Object.assign(this.$options.computed, {...this.dependencies.mapGetters(this.stores)});
console.log(this.$options.computed); // returns mapped getters
},
Console output returns mapped getters, but it doesn't seem to work, in Vue DevTools those Vuex bindings are undefined.
No. You can't. There's no way around.
Also,
The $options is read only.

Categories

Resources