How to add query param before navigation start in angular - javascript

Let's say I have two components A and B. Component A navigates to /a?random=random and B navigates to /b. I want to add authUser query param before navigation starts so end result should look like this /a?random=random&authUser=1, /b?authUser=1 respectively.
The problem is that url for component A looks like this /a?authUser=1 after appending the authUser queryParam not /a?random=random&authUser=1.
Here is the stackblitz link.
// App component
ngOnInit() {
this.router.events.subscribe((event) => {
if(event instanceof NavigationStart) {
if (!event.url.includes('authUser')) {
// remove queryParams
const url = event.url.split('?')[0];
if (url !== '/') {
// /a/etc => ['a','etc']
const newUrl = url.split('/').filter(_subUrl => _subUrl);
this.router.navigate(newUrl, { queryParams: { authUser: '1' }, queryParamsHandling: 'merge' });
}
}
}
})
}
navigateToA() {
this.router.navigate(['a'], { queryParams: { random: 'random' } });
}
navigateToB() {
this.router.navigate(['b']);
}
I don't wanna make changes to component A and B navigate methods because in real applications then i'll have to make changes to every navigate method which would be a bad practice.

exampe fo query paramthis.router.navigate([/${this.appStatus.baseUrl}/predict/result], {queryParams: {vsgid: vsgid, sid: sid, logo: logo}});

Related

Check if currentRoute starts with some text(something/something/*...) in Angular

I want to stop loader from loading from few screens and therefore I applied ngIf at routes where loader isn't needed. Here is the code for app.component.ts :
<router-outlet>
<app-spinner></app-spinner>
<ngx-ui-loader *ngIf="!(currentRoute =='/dashboard' || currentRoute == '/vehicle/edit/')"></ngx-ui-loader>
</router-outlet>
app.component.html
this.currentRoute = "";
this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationEnd) {
this.currentRoute = event.url;
}
});
I need to add * to vehicle/edit URL as there can be any vehicle ID while fetching the edit page like : /vehicle/edit/49042/1422, /vehicle/edit/49023/1421 and so on.
How to allow currentRoute accept /vehicle/edit/* ?
Ok, so to answer your question about route that accepts dynamic URLS/all URLS starting with /vehicle/edit/.
The "dummy" approach if you know that your nesting got limit, is to declare multiple routes with params, more-less like this:
const routes: Routes = [
{ path: '/vehicle/edit/', component: VehicleListComponent },
{ path: '/vehicle/edit/:id', component: VehicleEditComponent },
{ path: '/vehicle/edit/:parent/:id', component: VehicleEditComponent },
{ path: '/vehicle/edit/:grandparent/:parent/:id', component: VehicleEditComponent },
];
This will work, because Angular routing stops at very first matching path, so the order of your route declaration is important!
However, if you're dealing with very long nesting capability, better approach is to use custom route matcher:
import { UrlSegment } from '#angular/router';
const nestedCategoryMatcher = (url: UrlSegment[]) => {
// Check if this regex actually match your requirements
const regexMatcher = /^(vehicle\/edit)([\/][0-9]+.+)/;
if (!url.join('/').match(regexMatcher)) return null;
return ({ consumed: url });
}
const routes: Routes = [
{ path: '/vehicle/edit/', component: VehicleListComponent },
{ matcher: nestedCategoryMatcher, component: VehicleEditComponent },
];
And remember, that by using matcher, you will have to retrieve your "params" manually in your components by splitting URL into segments.
this.route.url
.subscribe(segments => {
const urlSegment: UrlSegment[] = (segments as UrlSegment[]);
console.log(urlSegment);
});

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.

How to get data value in regular js file from vue component?

I have component MyComponent.vue where I have data value that constantly changes. I want to pass this value to javascript file(js file should know about changes of value everytime)
Why do I do that? Because my regular js file is a service layer for axios methods. I can import this file in many other components. The file contains axios methods and urls are dynamic.
I want those urls depend on data variable. This data variable comes from MyComponent.js
So the main goal is to make dynamic urls of axios that depend on data variable
I tried some code but it doesn't work, because js file(CategoryService.js) know nothing about this.categoryNumber.
MyComponent.vue:
<script>
export default {
data() {
return {
categoryNumber: 1
}
}
}
</script>
CategoryService.js
import http from "../../../http-common";
let category = "category1";
if (this.categoryNumber === 1) {
category = "category1";
} if (this.categoryNumber === 2) {
category = "category2";
}
class CategoryService {
get(id) {
return http.get(`/${category}/${id}`);
}
update(id, data) {
return http.put(`/${category}/${id}`, data);
}
create(data) {
return http.post(`/${category}`, data);
}
delete(id) {
return http.delete(`/${category}/${id}`);
}
getAll() {
return http.get(`/${category}/all`);
}
}
export default new CategoryService();
So with a bit of refactoring, you could easily get this working.
First of all, I would put the if/else logic of your class into it.
For convenience and scalability, I would use a Vuex store that will keep track of your categoryNumber and share it accross all your components.
Then I would bind my service to my Vue instance so I can easily access it in all my components as well as the store and I would pass the latter to my class as a parameter.
For the last part, I don't know the logic in the http-common file so the code I will show you is a bit nasty. But depending on wether or not you bound 'http' to axios, you could make use of axios interceptors to call the getCategoryNumber() method in every request.
Here's an idea of the implementation I would go for:
const CategoryService = class CategoryService {
constructor(store) {
this._store = store;
this.category = "category1";
}
getCategoryNumber() {
if (this._store.state.categoryNumber === 1) {
this.category = "category1";
}
if (this._store.state.categoryNumber === 2) {
this.category = "category2";
}
console.log(this.category); // for demo puprose
}
get(id) {
this.getCategoryNumber(); // We could use axios request interceptor instead of calling that in every route, but that works !
return http.get(`/${this.category}/${id}`);
}
update(id, data) {
this.getCategoryNumber();
return http.put(`/${this.category}/${id}`, data);
}
create(data) {
this.getCategoryNumber();
return http.post(`/${this.category}`, data);
}
delete(id) {
this.getCategoryNumber();
return http.delete(`/${this.category}/${id}`);
}
getAll() {
this.getCategoryNumber();
return http.get(`/${this.category}/all`);
}
}
const store = new Vuex.Store({
state: {
categoryNumber: 1
},
mutations: {
setCategoryNumber(state, payload) {
state.categoryNumber = payload;
}
}
});
// Bind your service to the Vue prototype so you can easily use it in any component with 'this.$service'
// pass it the store instance as parameter
Vue.prototype.$service = new CategoryService(store);
new Vue({
el: "#app",
store, // dont forget to bind your store to your Vue instance
methods: {
updateCategoryNumber() {
// Put here any logic to update the number
this.categoryNumber = this.categoryNumber === 1 ? 2 : 1;
this.checkServiceCategoryValue();
},
checkServiceCategoryValue() {
// for demonstration purpose
this.$service.getCategoryNumber();
}
},
computed: {
// Look for the store value and update it
categoryNumber: {
get() {
return this.$store.state.categoryNumber;
},
set(value) {
this.$store.commit("setCategoryNumber", value);
}
}
}
});
<div id="app">
<h2>number: {{ categoryNumber }}</h2>
<button type="button" #click="updateCategoryNumber()">
updateCategoryNumber
</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex#2.0.0"></script>
Thanks to #Solar
I just added one more parameter for all urls and put the number of category to it
CategoryService.js:
class CategoryOneService {
get(id, category) {
return http.get(`/${category}/${id}`);
}
getAll(category) {
return http.get(`/${category}/all`);
}
}
functions.js:
let catNum = "";
function getQuestion() {
if (this.categoryNumber === 1) {
catNum = "category1";
}
if (this.categoryNumber === 2) {
catNum = "category2";
}
let questionId = this.questionNumber;
CategoryOneService.get(questionId, catNum)
.then(response => {
this.question = response.data.question;
this.answer = response.data.answer;
})
.catch(error => {
console.log(error);
});
}

Fetch Data using Vue http based on Router params

I have searched around for an answer to this and also followed the example on the vue router documentation but am still having issues. I am trying to do an http call on initial load of a component and then also watch the router params and update the 'get' call from vue-resource.
My vue component js looks like this...
export default {
name: 'city',
components: {
Entry
},
data () {
return {
city: null
}
},
mounted() {
this.fetchData();
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData() {
const cityName = this.$route.params.name;
this.$http.get('http://localhost:3000/cities?short=' + cityName).then(function(response){
this.city = response.data;
}, function(error){
alert(error.statusText);
});
console.log(cityName)
}
}
}
I have logged out the 'cityName' in my fetchData method and it always returns the right name, but when I append that 'cityName' to the http get call it is not returning the proper data. On initial load, this.city remains null and then each time I update the route, the data returns with the previous city selected instead of the new updated city in the route. I have tried Vue's created property in place of mounted and the same thing happens. Any ideas?
Try changing your fetchData method to the following:
fetchData() {
const cityName = this.$route.params.name;
this.$http.get('http://localhost:3000/cities?short=' + cityName).then((response) => {
this.city = response.data;
}, (error) => {
alert(error.statusText);
});
console.log(cityName)
}
The => function notation keeps this in context of the component.

history push url not reinitialise the component again in case of dynamic route change

I have a route defined as /:business_id/:branch_id/user. When I change the url from /1/1/user to /1/2/user using history.push('/1/2/user') the component is not re-rendered.
handleChange(val) {
const { history, route: { match: { path, params: { business_id, tabKey, mode }}}} = this.context.router;
let url = path.replace(':branch_id', val)
.replace(':business_id', business_id)
.replace(':tabKey', tabKey)
.replace(':mode', mode);
history.push(url);
}

Categories

Resources