Check if data in URL is valid before navigate to page - javascript

I would like to configure my Angular component, so that the page only loads if the ID in the URL is valid. The point here is, that I want to protect the page from users manually entering a random URL, and accessing any page.
I have a component with lists.
If I click on the "Show Details", Angular navigates to the details page. I would like to only open this page, if the entered URL contains a valid ID. To achieve this, I call a service to gather all IDs into an array of strings. And then examine if the entered ID is a member of that array.
What I have tried:
list.component.ts:
ngOnInit() {
this.fetchLists();
}
fetchLists() {
from(this.listService.getGroups())
.pipe(
takeUntil(this.destroy$)
)
.subscribe({
next: (listUI: ListUI[]) => {
this.listData = listUI;
},
error: (error) => {
this.logger.debug(error.message);
this.certError = true;
}
});
}
details.component.ts:
ngOnInit() {
this.fetchListsAndIDs();
if (this.validIDsList.includes(listID)) {
this.router.navigateByUrl(`/groups/lists/${listID}/details`);
}
else {this.router.navigateByUrl(`/groups/lists`);}
}
fetchListsAndIDs() {
from(this.listService.getGroups())
.pipe(
takeUntil(this.destroy$)
)
.subscribe({
next: (listUI: ListUI[]) => {
const listData = listUI;
this.validIDsList = listData.map((lists) => lists.id);
},
error: (error) => {
this.logger.debug(error.message);
this.certError = true;
}
});
}
app.routing.module.ts
{
path: 'groups/lists/${listID}/details',
component: DetailsComponent
}
The page "groups/lists/99999999999/details" opens, with zero data, and "this.validIDsList" is undefined. Can someone please help me how to fix this?

You almost have the right code, but you missed the part that, this.fetchListsAndIDs() is executing an asynchronous observable, so your if..else block is executing before even the API call completes.
I would suggest, you include the if...else check inside the next() handler. I have reversed the conditions to check for NOT first, since you are already in details.components.ts which represents ``/groups/lists/${listID}/details) route, you should only redirect the user back to lists if id is not valid, else the component should continue with its work.
I added code to grab the listId from URL. It is missing in the code you posted in the question.
ngOnInit() {
this.listID = this.route.snapshot.paramMap.get('listID');
this.fetchListsAndIDs();
}
fetchListsAndIDs() {
from(this.listService.getGroups())
.pipe(
takeUntil(this.destroy$)
)
.subscribe({
next: (listUI: ListUI[]) => {
const listData = listUI;
this.validIDsList = listData.map((lists) => lists.id);
this.handleNavigation();
},
error: (error) => {
this.logger.debug(error.message);
this.certError = true;
}
});
}
handleNavigation() {
if (!this.validIDsList.includes(this.listID)) {
this.router.navigateByUrl(`/groups/lists`);
} else {
// call the function to continue with details component
}
}

Related

Vue-cli project data update delays when sending request with Vuex and axios

I'm working on a project with Vue-CLI, and here's some parts of my code;
//vuex
const member = {
namespaced:true,
state:{
data:[]
},
actions:{
getAll:function(context,apiPath){
axios.post(`http://localhost:8080/api/yoshi/backend/${apiPath}`, {
action: "fetchall",
page: "member",
})
.then(function(response){
context.commit('displayAPI', response.data);
});
},
toggle:(context,args) => {
return axios
.post(`http://localhost:8080/api/yoshi/backend/${args.address}`,
{
action:"toggle",
ToDo:args.act,
MemberID:args.id
})
.then(()=>{
alert('success');
})
},
},
mutations:{
displayAPI(state, data){
state.tableData = data;
},
},
getters:{
getTableData(state){
return state.tableData
}
}
}
//refresh function in member_management.vue
methods: {
refresh:function(){
this.$store.dispatch('member/getAll',this.displayAPI);
this.AllDatas = this.$store.getters['member/getTableData'];
}
}
//toggle function in acc_toggler.vue
ToggleAcc: function (togg) {
let sure = confirm(` ${todo} ${this.MemberName}'s account ?`);
if (sure) {
this.$store
.dispatch("member/toggle", {
address: this.displayAPI,
id: this.MemberID,
act: togg,
Member: this.MemberName,
})
.then(() => {
this.$emit("refresh");
});
}
},
The acc_toggler.vue is a component of member_management.vue, what I'm trying to do is when ToggleAcc() is triggered, it emits refresh() and it requests the updated data.
The problem is , after the whole process, the data is updated (I checked the database) but the refresh() funciton returns the data that hadn't be updated, I need to refresh the page maybe a couple of times to get the updated data(refresh() runs everytime when created in member_management.vue)
Theoretically, the ToggleAcc function updates the data, the refresh() function gets the updated data, and I tested a couple of times to make sure the order of executions of the functions are right.
However, the situation never changes. Any help is appreciated!
The code ignores promise control flow. All promises that are supposed to be awited, should be chained. When used inside functions, promises should be returned for further chaining.
It is:
refresh:function(){
return this.$store.dispatch('member/getAll',this.displayAPI)
.then(() => {
this.AllDatas = this.$store.getters['member/getTableData'];
});
}
and
getAll:function(context,apiPath){
return axios.post(...)
...

Is there faster way about javascript autocomplete?

Goal : faster way autocomplete
Situation: I use vue js, when open the page, execute in mounted()
axios.get('http://127.0.0.1:3000/games/all')
.then(res=>{
this.$store.state.games = res.data;
this.$store.dispatch('GetGames',res.data);
})
.catch(err=>console.log(err))
axios get games/all is
router.get('/all',(req,res,next)=>{
let responseData = {};
Game.find({},(err,rows)=>{
if(err) throw err;
if(rows.length) {
responseData.result = 1;
responseData.data = rows;
}else{
responseData.result = 0;
}
res.json(responseData.data);
// console.log(responseData.data);
});
})
I got a json file like
{
{ gamename:"agricola",
maxplayer:4,
minplayer:1,
mintime:20,
maxtime:90,
difficulty:"hard",
}
}
There are 200~300 columns.
Using vuex
state: {
games:"",
},
mutations: {
getGames(state,payload){
state.games = payload;
console.log("받아온 게임",state.games);
},
}
GetGames:({commit},payload)=>{
commit('getGames',payload)
},
When I bring db, using state in store.js file, so I use autocomplete
const autocomplete = document.querySelector(".autocomplete");
if (this.gameInput) {
this.result = this.games.filter((game) => {
return game.gamename.match(new RegExp(this.gameInput, "i"))
});
} else {
autocomplete.classList.add("disabled");
}
the gameInput is your typing
Problem: it takes too long to bring the DB (4000ms~) and Data is stored in local storage. Even after receiving the data, I cannot search unless refresh it. so user has to stay until bring the db, and user cannot search game until refresh the page. I want they don't need to refresh my page. thanks for read my question. sorry about my poor English (tear)

How execute javascript after a component change in angular?

My code works fine when I write in browser localhost:4200/pay;id=1. This show Pay component with credit card fields generated by a external javascript (This javascript script is loaded from this component). But if i come from another component to this, Pay component doesn't show the credit card fields but load external script. How can I fix this?
My code
first.component.ts
let datos = {
id:'6'
}
this.router.navigate(['pay',datos]);
pay.component.ts
ngOnInit(): void {
this.loadScripts();
}
loadScripts() {
this.dynamicScriptLoader.load('2payjs').then(data => {
// Script Loaded Successfully
console.log('All elements loaded successfully')
this.loadElement();
}).catch(error => console.log(error));
}
loadElement(){
let that = this;
let id = this.router.snapshot.paramMap.get('id');
window.addEventListener('load', function() {
// Initialize the JS Payments SDK client.
let jsPaymentClient = new TwoPayClient('AVLRNG');
// Create the component that will hold the card fields.
let component = jsPaymentClient.components.create('card');
component.mount('#card-element');
// Handle form submission.
document.getElementById('payment-form').addEventListener('submit', (event) => {
event.preventDefault();
/// Extract the Name field value
const billingDetails = {
name: document.querySelector('#name').value
};
// Call the generate method using the component as the first parameter
// and the billing details as the second one
jsPaymentClient.tokens.generate(component, billingDetails).then((response) => {
//console.log(response.token);
let data = {
token:response.token
}
}).catch((error) => {
console.error(error);
});
});
});
}
const navigationExtras: NavigationExtras = {
queryParams: {
id: 1,
},
queryParamsHandling: 'merge'
};
this.router.navigate(['pay'], navigationExtras);
you need navigationExtras in order to create params in your router link and able to fetch by another component
Already solved. I just delete window load event listener.

Stripe not being called

I am trying to use Vue.js for my front end to call Stripe and create a token which then is sent to my backend. I have tested everything using plain HTML/JS and it all works fine, my issue comes in trying to use Vue.js I think my issue might be in how I am binding the stripe public key. Below is my code, and I have zero output to speak of, I get just redriected to the same page but wth ? at the end of the URL. Nothing else, console shows nothing and no error message or anything send to my back end.
template code
There is more but not related
<div class="col-md-8">
<card class='stripe-card col-md-8'
:class='{ complete }'
:stripe='stripeKey'
:options='stripeOptions'
#change='complete = $event.complete'
/>
<button class='pay-with-stripe' #click='pay' :disabled='!complete'>Submit Payment Details</button>
<br>
</div>
script section with relavent added
import { Card, createToken } from 'vue-stripe-elements-plus'
import axios from 'axios';
export default {
components: { Card },
data() {
return {
errorMessage: null,
successMessage: null,
complete: false,
stripeKey: process.env.VUE_APP_STRIPE_PUB_KEY,
stripeOptions: {
// see https://stripe.com/docs/stripe.js#element-options for details
hidePostalCode: true
},
current: {
stripe: {
plan: null,
last4: null
}
},
}
},
methods: {
pay () {
createToken().then(result => {
axios.post('/billing/updateCard', {
token: result.token,
})
.then(res => {
if(res.data.success == true) {
this.successMessage = res.data.message
console.log(res.data.message)
}
if(res.data.success == false) {
this.errorMessage = res.data.message // Display error message from server if an error exists
}
})
.catch((err) => {
if(err) console.log(err)
if(err) this.$router.push('/company/settings?success=false')
})
});
}
}
}
</script>
I have checked that the API key is actually in the data value by doing <p>{{ stripeKey }}</p> and seeing the value show up. So yes the key is there and the key is valid (tested copy/paste into my HTML/JS test)
created(){
this.key=process.env.VUE_APP_STRIPE_KEY;
}
try this, i used this piece of code in my project and it worked... the issue maybe is that your key is not yet initialized when card us rendered idk. maybe key isnt issue at all. try this and let me know if works and we will debug it together.

AngularFire2 Firebase update object without causing other subscriptions to update

I just have a question to find out if this is possible:
So what I am doing is when I submit a post I wait for it to complete then update the user object in firebase to insert a time-stamp.
This is fine and works but when the time-stamp is inserted it is causing other subscriptions that are subscribed to changes in the user object to update.
What I want to do it update the user object without causing other subscribers to be updated.
Here is where I am updating the timestamp:
I tried commenting out this line of code which stops the data duplication issue on screen but I need this code to run as I need to update the timestamp when a post is submitted.
this.af.database.object('users/' + x[0].uid + '/lastPostAt').set(timestamp)
.then(x => { this.dialogsService.showSuccessDialog('Post Submitted'); });
Here is where I am subscribing to all the posts:
subscribeAllPosts(): Observable<Post[]> {
return this.af.database.list('/posts')
.map(Post.fromJsonList);
}
Here is where I am creating the array of posts in my constructor to display via a loop in the html:
this.activeItem = this.items[0];
this.postsService.subscribeAllPosts()
.subscribe(posts => {
let container = new Array<PostContainer>();
for (let post of posts) {
this.getEquippedItemsForUsername(post.username).subscribe(
x => {
try {
container.push(new PostContainer(post, x[0].equippedItems));
} catch (ex) { }
}
);
}
this.postContainers = container;
});
In the inner subscription it gets the equippedItems for the user of the post:
getEquippedItemsForUsername(username: string) {
return this.usersService.subscribeUserByUsername(username);
}
Which in turns calls:
subscribeUserByUsername (username: string) {
return this.af.database.list('users' , {
query: {
orderByChild: 'username',
equalTo: username
}
});
}
In the HTML it loops through the postContainer[]:
<li *ngFor="let item of postContainers">
So all that works the issue as stated is that if I do not have the following commented out then the posts will be duplicated more and more as the posts are submitted. If I refresh the app then the posts will show the correct non duplicated posts until another post is submitted.
this.af.database.object('users/' + x[0].uid + '/lastPostAt').set(timestamp)
.then(x => { this.dialogsService.showSuccessDialog('Post Submitted'); });
EDIT: Solved by splitting up logic.

Categories

Resources