what is the equivalent of watchEffect in VueJS 2 - javascript

In a VueJS 2 component, I populate a props variable when the component is loaded.
created() {
this.events = null
Service.getEvents()
.then(response => {
this.events = response.data
})
.catch(error => {
console.log(error.response)
})
}
}
I need to reload the component with "events" props when a next page is clicked. I saw a VueJS 3 tutorial, where the props variable is reloaded using the watchEffect method (https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watcheffect)
What is the equivalent of this functionality in VueJS 2?
I tried to use the watch() method for this variable, but that causes an infinite recursion because the "events" object is changed inside the method.
//Error - recusrsion
watch: {
events() {
this.events = null
Service.getEvents()
.then(response => {
console.log(response.data)
this.events = response.data
})
.catch(error => {
console.log(error.response)
})
}
},
How can we reload the events in the same page when user clicks a button/link?

on your code this.events mean the variable of watch...
so it cause recursion.
if you want to add event on click next page,
then add #click= event props on component of next page props

watch in the options api receives the old and the new value:
watch: {
event: function (oldEventData, newEventData) {
console.log(newEventData) // do something with new event data
}
},

Related

Re-creating a method which is already in component inside a store js file in Vue

This can be a simple question but since I'm in a learning curve of vuejs I'm struggling to achieve following task.
I have this method inside one of my vue component.
async loadSuggestedUsers() {
if (!this.suggestionUrl) return false
else this.isSearching = true;
this.cancelPreviousRequest();
await axios.get(`/${Helper.getLocale()}/dashboard/${this.suggestionUrl}`, {
params: {
page: this.suggestedUsersData.page,
per_page: this.suggestedUsersData.perPage,
search_text: this.searchText,
nationality: this.selectedNationalities
},
cancelToken: this.requestToken.token
}).then(({data}) => {
collect(data.data).each((user) => {
if (!this.suggestedUsers.some(obj => obj.id === user.id)) {
this.suggestedUsers.push(user)
} else if (
user.project_specific_job_title_id &&
!this.suggestedUsers.some(obj => obj.project_specific_job_title_id === user.project_specific_job_title_id)
) {
this.suggestedUsers.push(user)
}
})
this.suggestedUsersData.perPage = data.per_page
this.suggestedUsersData.total = data.total
this.isSearching = false
}).catch(error => {
if (error instanceof axios.Cancel) return false;
});
},
but now, Instead of directly calling this method from the component I'm trying to access this by using store .
Under my store, I have a js file called, TestStore.JS
My questions are,
How can I re-create above method in my TestStore.JS and how call that recreated method in my component...
One solution is to create a state for this function in your store and pass its reference to it in the created lifecycle of the component.

How to refresh a selector?

I have 2 components which is Component A and Component B , Component A is calling component B via selector , the app-team-users is the selector , it is calling data from API and then populating it on the table which is working fine.
In Component A I called the component B selector , in Component A I have a feature where I create a user or save a user .
How do we refresh or call the app-team-users selector so that if I save a user from Component A then the table on Component B will refresh which is the app-team-users will refresh ? cause they are seperate component. Maybe anyone has idea or enlightenment ? Thank you very much. Good day.
#Component A save user code
saveUser() {
if(this.createUserForm.valid) {
this.accountService.create(this.createUserForm.value).pipe(
finalize(() => {
this.isInProgress = false;
})
).subscribe({next: (res) => { this.notificationService.showSuccess('User has been created successfully.');
//if user is created refresh the selector
},
error: (err) => {this.notificationService.showError('Something went wrong, Try again later.');
this.isInProgress = false;
},
complete: () => {
this.isInProgress = false;
},
});
}
}
app-team-users table
#Code for calling the selector Component A
<form [formGroup]="modelForm" >
<app-team-users [selectedNewUser] = "newUsers" [selectedTransactionTeam]="modelForm.value.teams"
[selectedTransactionUser]="modelForm.value.users" (transactionUserEvent)="transactionUserEvent($event)"
(transactionTeamEvent)="transactionTeamEvent($event)">
</app-team-users>
</form>
#app-team-users code
ngOnInit(): void {
this.transactionUserTable.dataSource = new MatMultiSortTableDataSource(this.sort, this.CLIENT_SIDE);
this.transactionUserTable.nextObservable.subscribe(() => { this._transactionUserPageEvent(); });
this.transactionUserTable.sortObservable.subscribe(() => { this._transactionUserPageEvent(); });
this.transactionUserTable.previousObservable.subscribe(() => { this._transactionUserPageEvent(); });
this.transactionUserTable.sizeObservable.subscribe(() => { this._transactionUserPageEvent(); });
}
private _transactionUserPageEvent() {
this.isTransactionUserLoading = true;
this.transactionUserTable.data = [];
this._userProfileService.getUserProfileTableDropdown(
this.accountId,
this.transactionUserTable.pageIndex + 1,
this.transactionUserTable.pageSize,
this.searchTransactionUserInput.nativeElement.value,
this.transactionUserTable.sortParams,
this.transactionUserTable.sortDirs
)
.pipe(
finalize(() => this.isTransactionUserLoading = false)
)
.subscribe({
error: err => this._notificationService.showError(err),
next: res => {
this.transactionUserTable.totalElements = res.totalItemCount;
this.transactionUserTable.data = res.items as UserProfileDropdownDto[];
this.totalData = res.totalItemCount;
this.currentDisplayedData = res.lastItemOnPage;
},
complete: noop
});
}
You can do it via Observables.
Emit a value to an observable when a user is created in the first place. You can either pass the entire user value inside the observable and use that data to populate the second component from the front side itself. Or you can just pass a value, just to know in the second component that something has been created in the first one, then you call the same api to populate in the second component.

Where should I put function which would be used by two or more Vue.js components?

I have a function which takes an ID as an argument and finds the object to which this ID belongs from a JSON that is stored in the Vuex store. So far I have used the function in 1 component only, however, I recently created a second component which also requires that function. Currently I have just copied and pasted the function, however, this seems to be less than optimal. This is why I wonder where should this function be placed so that it's accessible from all components that need it.
I've been wondering if the Vuex store is a viable place to store the function but I'm not too sure so I decided to ask for your advice. Thanks
findChampionName(id){
let championId = id.toString();
let champion = Object.entries(this.$store.state.champions).find(([key,value]) => value.key === championId);
return champion[1]
}
Vuex store:
export default new Vuex.Store({
state: {
champions: null
},
mutations: {
champions(state, data){
state.champions = data.champions
}
},
actions: {
getChampions({commit, state}){
axios.get("https://ddragon.leagueoflegends.com/cdn/9.14.1/data/en_US/champion.json")
.then((response) => {
commit('champions', {
champions: response.data.data
})
})
.catch(function (error) {
console.log(error);
})
}
}
})
As far as I can see, you should use a vuex getter for your case:
getters: {
getChampionName => state => id => {
let championId = id.toString();
let champion = Object.entries(state.champions).find(([key,value]) => value.key === championId);
return champion[1]
}
}
And you can access that getter by passing the id: this.$store.getters['findChampionName'](id)

Array at index returning undefined in Angular ngAfterViewInit

Using Angular 7 and .NET Core 2.2, I'm working on a financial application and a watchlist for it. Symbols are added (and saved to a database). When the watchlist component loads, in ngOnInit, I get the data pulled from my resolver via router. Then I call a function which takes an array, loops through them, and returns a different data set from the financial data source. I then push each one of those returned items from the Observable into a local array watchListItems: IEXStockPrice[];
My code is:
ngOnInit() {
this.router.data.subscribe(data => {
this.watchListItemsFromDb = data['watchListItems'];
if (this.watchListItemsFromDb) {
this.fetchLatestPriceforWatchListItemsFromIEX(this.watchListItemsFromDb);
}
});
}
ngAfterViewInit(): void {
console.log(this.watchListItems);
console.log(this.watchListItems[0]);
}
fetchLatestPriceforWatchListItemsFromIEX(items: WatchListItemFromNet[]) {
if (!this.watchListItems) {
this.watchListItems = [];
}
items.forEach(element => {
this.iex.getStockQuoteBySymbol(element.symbol)
.subscribe(
(iexData: IEXStockPrice) => {
this.watchListItems.push(iexData);
},
(err: any) => this.alertify.error('Could not get the latest data from IEX.')
);
});
}
Now, I have a child component LineChartComponent which needs to create a line chart/graph of the active watchlist item. I was just going to first load the chart component with the first element in the watchListItems array. I can't get access to it in ngOnInit because the component hasn't finished initializing.
I've tried ngAfterViewInit.
However, as you can see in the picture, the watchListItems has content in it, but trying to access the first element returns undefined.
Since the router provides its data as an Observable, I would only call fetchLatestPriceforWatchListItemsFromIEX() if watchListItemsFromDb has been populated
ngOnInit() {
this.router.data.subscribe(data => {
this.watchListItemsFromDb = data['watchListItems'];
if (this.watchListItemsFromDb) {
this.fetchLatestPriceforWatchListItemsFromIEX(this.watchListItemsFromDb);
}
});
}
fetchLatestPriceforWatchListItemsFromIEX(items: WatchListItemFromNet[]) {
// initialize your array here vs in the viewmodel
if (!this.watchListItems) {
this.watchListItems = [];
}
items.forEach(element => {
this.iex.getStockBySymbol(element.symbol)
.subscribe(
(iexData: IEXStockPrice) => {
this.watchListItems.push(iexData);
},
(err: any) => this.alertify.error('Could not load watchlist')
);
});
}
Then in your component, you can check for the array, then pass the first item to your child component as an #Input:
<ng-container *ngIf="watchListItemsFromDb">
<child-component [data]="watchListItemsFromDb[0]"></child-component>
</ng-container>

VueJS list rendering anomaly

I already read some similar topics about my problem, but still I can’t figure out, what can be the problem is.
Long story short, I have an array of objects, and these objects also have an array, called “messages”. The parent object has some additional data, and my problem is caused by the child array, the messages.
The problem is:
The messages array is filled up by the server. After it, I successfully render a list of it’s content, show it on the web page, so far so good. But as soon as a new data arrives from the server, the rendered list is not updating itself. I checked the array’s content via the Vue dev plugin, and the new message was there, but the rendered list still remained in it’s original state.
The twist in the story is, that I created a simple test array, which is very similar to the original one, and also rendered on the page. When a new message arrives, I push it to both of them, and to my surprise, each one is updating itself perfectly. For the sake of curiosity, I commented out the test array rendering, and believe it or not, the original array does not update itself anymore on the page. After I remove the commented test array render, both of them works again.
The vue component:
/**
* Root
*/
import Vue from 'vue'
import axios from 'axios'
import inputComponent from './chat/input.vue'
new Vue({
el: '#chat-root',
components: { inputComponent },
data() {
return {
userID: document.getElementById('user').value,
ready: false,
roomID: "",
roomInstances: [],
test: [
{
messages: []
}
],
pollTime: 5000
}
},
created() {
axios.get('chat/init')
.then(resp => {
this.roomID = resp.data.roomList[0]._id
this.roomInstances = resp.data.roomList
this.fetchMessagesFromServer()
this.poll()
this.heartBeat()
}).catch(err => console.log(err))
},
computed: {
getMessages() {
return this.roomInstance.messages
},
roomInstance() {
return this.roomInstances.filter(i => i.id = this.roomID)[0]
}
},
methods: {
setRoom(id) {
if (this.roomID === id)
return
this.roomID = id
},
fetchMessagesFromServer() {
axios.get(`chat/get/${this.roomID}`)
.then(resp => {
this.roomInstance.messages = resp.data.messages
this.ready = true
}).catch(err => console.log(err))
},
heartBeat() {
setInterval(() => axios.get(`heartbeat/${this.userID}`), this.pollTime - 1000)
},
poll() {
axios.create({ timeout: this.pollTime })
.get(`poll/${this.userID}`).then(resp => {
if (resp.data.data) {
this.roomInstance.messages.push(resp.data.data.message)
this.test[0].messages.push(resp.data.data.message)
}
this.poll()
}).catch(err => console.log(err))
}
}
})
Template:
extends base
block scripts
script(src="/dist/chat.bundle.js")
block content
#chat-root(style="margin-top: 50px" v-cloak)
// Hack! Supposed to be an axios request?
input(v-if="false" id="user" value=`${user._id}`)
input-component(:room="roomID")
p {{ roomID || "Whoo hooo hoooo!" }}
span(v-for="room in roomInstances" #click="setRoom(room._id)") {{ room.name }} |
div(v-if="ready")
div(v-for="message in getMessages")
p {{ message }}
hr
div(v-for="fe in test[0].messages")
p {{ fe }}
For one, you are not finding the roomInstance correctly in the function for that computed property. You should just use the find method and return i.id === this.roomID in the callback instead of i.id = this.roomID:
roomInstance() {
return this.roomInstances.find(i => i.id === this.roomID);
}
The other issue might be when you are setting roomInstance.messages in the axios.get().then callback. If roomInstance.messages is undefined at that point, you'll need to use the Vue.set method (aliased on the Vue instance via this.$set) in order to make that property reactive:
this.$set(this.roomInstance, 'messages', resp.data.messages)
Here's the Vue documentation on Change Detection Caveats.

Categories

Resources