Computed property get returned twice - javascript

I want to get the initials of the full name using computed properties but it returns undefined and tries to get the initials before it renders the full name as shown below
computed: {
user: function () {
console.log(this.$store.state.user.name);
//return this.$store.state.username.match(/[A-Z]/g).slice(0, 2).join('');
}
}
How to get this to work? like it doesn't call match function unless the full name is rendered?

The way the computed property is running twice, is once when the app is created, then once your data updates, running twice.
Best way to handle this is wherever your data is to change the computed property to check if the data is there, like so:
if(this.$store.state.user.name) return this.$store.state.user.name
Or if you are placing your computed property to the template, use a v-if:
v-if="$store.state.user.name"

You can use Optional Chaining to handle undefined cases within your return statement.
MDN
computed: {
user: function () {
return this.$store.state?.user?.name?.match(/[A-Z]/g).slice(0, 2).join('');
}
}

Related

Vue.js computed property to parse/format data

I am trying to get a Vue.js computed property to do some reconfiguring of data and when I do it inside the computed property I get an error about side effects. I looked it up on here and found that using a method is the correct way of doing this. Now when trying to use a method it results in error messages in the console about it can't read the property of 'units' from selectedBuilding.
What I am trying to figure out is how to have it so the units array is populated with the correct data depending on the computed return value
Below is the code stripped down for the script section of my Vue.js file.
export default {
name: "Form",
data: () => ({
building: {},
buildings: [
{
"units": [ // Bunch of data in here that isn't important//],
"buildingID": "<an ID according to a custom format>",
}],
units: [],
}),
methods: {
formatUnits: function(selectedBuildingID) {
let selectedBuilding = this.buildings.find(building => building.buildingID === selectedBuildingID)
this.units = selectedBuilding.units
return true
}
},
computed: {
useExistingBuilding() {
if(this.building === 'New') {return true}
else {
this.formatUnits(this.building)
return false
}
},
}
};
</script>
Error message:
TypeError: Cannot read property 'units' of undefined
Since this.building has no buildingID (thus, undefined), and there is no item in buildings that has undefined as the value for buildingID, it fails.
This is because the find method will return undefined when the condition couldn't be satisfied.
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
MDN Reference
In the computed property you are passing the whole building rather than the id, whereas in your method (in find) you are comparing the ids.
You need to pass the building id to your method.
Also in your data you are declaring building as an object, and then in your computed property, you are asserting whether building equals New which is a string.
I would advise the first thing you have to do is rethink your data types and the flow of your data between the template, computed properties, methods and data.

Angular Http subscribe - cannot assign to variable in component

I want to retrieve data from api and assign it to some value inside the angular component. In subscribe I'm trying to assign the data to loggedUser and then call function inside this subscribe to navigate to another component with this received object. Unfortunately I got the error : The requested path contains undefined segment at index 1. I want to have this object set outside the subscribe too. How can I achieve this?
logIn() {
this.portfolioAppService.logIn(this.loggingUser).subscribe((data) => {
this.loggedUser = data;
console.log(this.loggedUser);
console.log(data);
this.navigateToProfile(this.loggedUser.Id);
});
}
navigateToProfile(id: number) {
this.router.navigate(['/profile', id]);
}
console output
You are using an incorrectly named property when calling navigateToProfile.
From your console output, I can see that the data object in the subscribe looks like this:
{
id: 35,
// ..
}
But you are calling the function like this:
this.navigateToProfile(this.loggedUser.Id);
Instead, use the property id (lower case)
this.navigateToProfile(this.loggedUser.id);
To narrow this problem down in the future, try being more specific in your testing. Humans are good at seeing what they want to see and will assume the problem is more complicated than it is. If you had tried console.log(this.loggedUser.Id), you would have seen the result undefined, and worked out the problem yourself.

If the same value is being printed on the console before calling axios, why is it that a different value gets sent to the server?

I have a form field that has the following generic autocomplete:
<generic-autocomplete
v-model='c.heuristicId'
v-on:change='heuristicSelected(i, $event)'
label='Fuente de datos'
:apiFunction='heuristicsFetchFunction'
:disabled='disabled'/>
When saving the form and sending it with this field in blank heuristicIdgets sent as nullwhich is what is desired.
However, when selecting a value from the generic autocomplete and then deleting it, heuristicIdgets sent with the id of the previously selected value.
Inside the function that calls axios I'm logging console.log("heuristicId to be sent: " + campaign.heuristicId);and undefinedgets logged in both scenarios.
EDIT: After inquiring a little bit more I believe that the error might be in one of <generic-autocomplete/>'s computed properties:
isValid: function () {
// this checks that the name in display equals the object selected in
// memory so no invalid labels are displayed while a valid object is 'selected'
let vc = this
let result = false
if (vc.object) {
result = vc.objectName === vc.object.name
console.log('generic-autocomplete#isValid', result)
}
return result
}
An empty vc.objectwill return a falseresult and I haven't been able to satisfactorily handle that.
After logging a really complex control flow I finally got to the point where I knew which properties I had to mutate. However, it turns out that in Vue 2 mutating inline properties is an anti-pattern. The capability of mutating inline props was deprecated from the previous Vue version in order to be consistent with the library's fully reactive paradigm. So what happens is that if you try to mutate an inline property, when the component re-renders, such property will be set back to its default (or previous) value.
This is solved by adding a computed property that's a copy from the property that you want to mutate.
//Somewhere else in the code the property is defined
property: null
//Add the computed property
data: function () {
return {
mutableProperty: this.property,
}
}

How vuejs knows the depenedencies of computed property for caching?

I have this Vue.js code:
new Vue({
data:{
myValue:'x',
myOtherValue:'y'
},
computed: {
myComputed: myFunction(){
return this['my' + 'Value']
}
}
})
As you can see the computed property will be cached and it is depended only on data.myValue. My question is how Vue.js caching system knows that run the computed function again only if myValue is changed?
If I change the myOtherValue variable, the myComputed function will use the cache, and will not be run again will I call it.
I thought about several ways how it is possible. But how Vuejs doing that?
I have read this article: https://v2.vuejs.org/v2/guide/computed.html and found no answer.
And what happen in this code, what it will be depeneded on?
const flag=2
new Vue({
data:{
myValue:'x',
myOtherValue:'y'
},
computed: {
myComputed: myFunction(){
if (flag==1){
return this['my' + 'Value']
}
else
return this['my' + 'Other' + 'Value']
}
}
})
Bonus: I will appreciate I link to the relevant function in the VueJS code: https://github.com/vuejs/vue
I will address only the specific question how does vue.js know which dependencies affect which computed property?
The simple answer is that each time vue evaluates a computed property it creates a map of all the reactive properties that were accessed in the span of that call. The next time any of these reactive properties change they will trigger a reevaluation of the computed property.
If during the most recent evaluation of a computed property, one of its reactive dependencies is never reached (maybe because it is within the non-traveled path of an if/else construct), subsequent changes to that reactive property will not trigger a reevaluation of the computed property.
Observe this behavior by modifying the two reactive properties in this fiddle (by simply typing in their corresponding input boxes). A few things to note:
the called computed property is evaluated once on document load (it's triggered because it's rendered in the template).
because the path is set to 1 the reactive property that will be mapped as a dependency is val1. As a result it will be the only one that can trigger a reevaluation of called when it changes. The value of val2 can also change but will not have the same effect on called, even though it's clearly present in the function.
When you click on the "Change Path" button, path is toggled from 1 to 2.
right after the path switch, note that a change to val1 will affect called only once more. Because path has been set to 2 prior to that last reevaluation, val1 will not be reachable and will not be mapped as a dependency of called any longer. Subsequent changes to its value won't trigger a reevaluation of called from that point on. But then val2 has now been mapped as a dependency of called and changes to it trigger the reevaluation the same way they did for val1 earlier. It will be so until the next path toggle from 2 back to 1.
Here's the code.
let path=1
let count=0
const vm=new Vue({
el:"#app",
data:{
val1:null,
val2:null,
},
computed: {
called: function(){
if (path==1){
this.val1
}
if (path==2){
this.val2
}
return "I was just called "+ ++count +" times"
}
},
methods: {
changePath(){
path = path==2 ? 1 : 2
}
}
})
and corresponding template
<div id="app">
<input v-model="val1"/> {{val1}}
<br>
<input v-model="val2"/> {{val2}}
<br>
<button #click="changePath">change path</button>
<br>
{{ called }}
</div>
It's the reactivity system of Vue.js, not a caching system.
The data in a component will be convert to getters and setters. When you access a value via a getter, the getter will add it to the dependencies, and when you modify the value via a setter, the setter will notify everyone who depends on the value.
Here is the source code, all the magic happens in this function: https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js#L131
From the docs it reads that:
Computed properties are cached, and only re-computed on reactive dependency changes.
However the following fiddle shows something a bit different.
https://jsfiddle.net/z11fe07p/267/
From the fiddle if you set the flag to 2, the computed property will be re-evaluated and executed if you change myOtherValue, however this will not happen if the flag is set to 1. I think it keeps track of your if conditions.
In the docs usually you can find links to the relevant source code.
Here is the code for computed properties:
https://github.com/search?utf8=%E2%9C%93&q=repo%3Avuejs%2Fvue+extension%3Ajs+%22computed%22&type=Code

_.keys not finding every Item shown in Firebug

I have a post, saved to $scope.mydata in the controller. I give this data via a parameter to a service that returns a function.
The service:
module.factory('myservice', function () {
return function servicefunction(mydata) {
var test = _.keys(mydata).length;
console.log("mydata", mydata);
console.log("test", test);
Firebug in Firefox shows me in the dom that mydata contains:
Object { $$state={...}, angularCollection=false, $object={...}, mehr...}
and the test returns 5.
When I type
module.factory('myservice', function () {
return function servicefunction(mydata) {
var test = _.keys(mydata.$$state).length;
console.log("mydata", mydata.$$state);
console.log("test", test);
Firebug gives me the Object { status=0} and in the DOM there is status and value []. I need to get access to that value. The test returns 1 for the status.
Why test via _.keys finding the status but not the value?
It looks like you are trying to access a property (value) that is inherited from your mydata.$$state's prototype. In that case, using _.keys will never list value.
It may very well be that it is inherited for now because it is empty, and it will become an own property once it is set.
Why not simply accessing it like so: mydata.$$state.value? It should work for both cases (inherited and owned).
I may lack a major part of your context, so if it still does not work, it would be worth providing more information about your data.

Categories