How to refresh a selector? - javascript

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.

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.

Angular rxjs data not updating correctly when using share , map & filter

Based on searchCriteria collected from a form, I have a method that return observable of talents.
getTalents(searchCriteria) {
return this._allUsers$.pipe(
tap((talents) => console.log(talents)),
map((talents: any) => {
let filtered = talents.filter(
(t) =>
(t.userType === 'talent' || t.userType === 'both') &&
this.matchOccupationOrSkill(
searchCriteria.occupation,
t.occupation,
t.skills
)
);
if (searchCriteria.loggedInUserId) {
filtered = filtered.filter(
(t) => t.id !== searchCriteria.loggedInUserId
);
}
return filtered;
})
);
}
where _allUsers$ is a shared observable,
this._allUsers$ = firestore.collection<any>('users').valueChanges().pipe(share());
I am calling getTalents inside ngOnInit method of a route component TalentSearchResult.
ngOnInit(): void {
this.subs.add(
this.route.queryParams.subscribe(params => {
if (params.action==='homesearch' || params.action==='jobsearch'){
this.criteria = {
occupation:params.occupation,
industry:params.industry,
loggedInUserId:params.loggedInUserId,
}
this.dataService.getTalents(this.criteria).pipe(take(1)).subscribe(talents => {
this.count = talents.length;
if (this.count >0){
this.Talents = talents.sort((p1,p2)=> p2.createdate-p1.createdate);
}
})
}
})
)
}
Search works for the first time, then when I go back (using brower back button) and search with a new criteria, it routed correctly to the TalentSearchResult component, however, stale list is being returned. I debugged and found that line 3 in the first code section above, i.e. tap((talents) => console.log(talents)) already logs in the stale value, even before the pipe/map/filter is executed !
Please advice
If you are using _allUsers$ or dataService.getTalents() elsewhere in the app, then you should replace share() with shareReplay(1). Using share() allows multiple subscribers to an observable, but any late subscribers won't get any data until .valueChanges() emits a new value. Using shareReplay(1) will allow late subscribers to immediately get its last cached value.
If you're certain this is the only component subscribing to these observables, then it could be that your component isn't closing subscriptions properly. This may be caused by the nested subscriptions (an anti-pattern) you have inside your ngOnInit() event. I would recommend changing this logic to utilize operators:
this.subs.add(
this.route.queryParams.pipe(
filter(({ action }) => ['homesearch', 'jobsearch'].includes(action)),
map(({ occupation, industry, loggedInUserId }) => ({ occupation, industry, loggedInUserId })),
switchMap(criteria =>
this.dataService.getTalents(criteria).pipe(take(1))
),
filter(talents => !!talents.length),
tap(talents => this.Talents = talents.sort((p1, p2) => p2.createdate - p1.createdate))
).subscribe()
);

what is the equivalent of watchEffect in VueJS 2

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
}
},

How to update a behaviour subject after patch request?

How to update the view with new values after making a patch request?
I have created a dummy app on stackblitz.
https://stackblitz.com/edit/angular-j35vrb
On clicking any row from the table, a modal opens and status of the TODO is updated. If the status is incomplete = false, patch request is sent and status is updated to true (can be seen in the console log).
How do i inform the behaviourSubject (through which data is being fetched) that there is some update in the content? so that the table as well as the top section (No. of complete/incomplete Todos) shows updated values
I have updated your stackblitz with solution. Here is what you need to do.
In main.service.ts,
Keep a reference of todos within the service itself for future updates. See below.
private allTodos: Todo[];
getTodo() {
if(!this.allTodos$){
this.allTodos$ = <BehaviorSubject<Todo[]>>new BehaviorSubject(new Array<Todo>());
this.http
.get<Todo[]>(
'https://jsonplaceholder.typicode.com/todos'
).subscribe(
(data: Todo[]) => {
this.allTodos = data;
this.allTodos$.next(data);
}
),
error => console.log(error);
}
}
Return the BehaviourSubject directly from subscribetoTodos method. Subjects can be subscribed directly.
subscribetoTodos(): Observable<Todo[]>{
return this.allTodos$;
}
update 'updateToDo' method as below. Notice how I am updating one reference of Todo in the list here and sending it to subscribers via next.
updateTodo(id){
this.http.patch('https://jsonplaceholder.typicode.com/todos/' + id , { 'completed': true })
.subscribe(
data => {
this.allTodos.find(todo => {
if (todo.id === id) {
Object.assign(todo, data);
return true;
}
return false;
});
console.log(data);
this.allTodos$.next(Object.assign([], this.allTodos));
},
error => console.log(error)
)
}
This will update your view.
In case of paginated data, change the subscription to the below.
ngOnInit() {
this.todos$ = this.mainService.subscribetoTodos();
this.todos$.subscribe(
(data)=>{
this.page = this.page || 1;
this.pageSize = 10;
}
)
}
Notice how I am checking if this.page exist, then use that, else go to page 1.
this.page = this.page || 1;
Earlier it was,
this.page = 1;
which was resetting the page back to 1 whenever any update happened.

Angular 5 activatedroute with having to nest service call in the subscribe

I have a angular 5 app with routing in which my app.routes.module.ts file handles the route for criminal
{ path: 'criminal', component: CriminalComponent, pathMatch: 'full'},
However, in navigating to here, I'm upgrading an old site to angular and I'm for now using the generated URL links example:
<td href="/criminal/?UserID=67694&AppID=4&AppGroupID=13&AM&LoginKey=202088840" >CR Programmers</a></td>
I wasn't sure if I should be tampering with the route file for all these querystring parameters so for the MENU generation I was placing it in there.
ngOnInit(): void { .... }
So inside OnInit I know I need to parse out the querystring
Then I need to make a service call passing in all the params
When I had them separate, it seems that conditional if on if there WAS a menu was NOT processed in time and no menu service call would happen.
Is there a way to have my service call subscribe to the query params section?
this.activatedRoute.queryParamMap
.map((params: Params) => params.params)
.subscribe((params) => {
if (params && params['UserID']) {
let UserID = params['UserID'];
let AppID = params['AppID'];
console.log('UserID', UserID);
console.log('AppID', AppID);
this.hasMenu = 1;
console.log('hasmenu', this.hasMenu)
this.arsSevice.getMenu() //will pass params
.subscribe(
result => {
this.menuA = result["data"].Menu1Items
this.menuB = result["data"].Menu2Items
this.menuC = result["data"].Menu3Items
this.menuD = result["data"].Menu4Items
this.menuE = result["data"].Menu5Items
},
error => {
console.log('menu error', error);
})
}
});
Basically above code has the getMenu() call ( which is not currently passing params)
I would LIKE to not have it NESTED inside
this part i would prefer to do like this
if (this.hasMenu == 1) {
this.arsSevice.getMenu()
.subscribe(
result => {
this.menuA = result["data"].Menu1Items
this.menuB = result["data"].Menu2Items
this.menuC = result["data"].Menu3Items
this.menuD = result["data"].Menu4Items
this.menuE = result["data"].Menu5Items
},
error => {
console.log('menu error', error);
})
}
You can use filter to do conditional checking and flatMap/map to chain them up
this.activatedRoute.queryParamMap
.map((params: Params) => params.params)
.filter((params) => params && params['UserID'])
.flatMap(params=>{
..... your other operation
return this.arsSevice.getMenu()
}).map( result => {
this.menuA = result["data"].Menu1Items
this.menuB = result["data"].Menu2Items
this.menuC = result["data"].Menu3Items
this.menuD = result["data"].Menu4Items
this.menuE = result["data"].Menu5Items
}).subscribe()

Categories

Resources