how to unsubscribe () a firebase collection in a vue.js component - firebase

In a vuejs component which dynamically retrieves data with firebase I would like to unsubscribe when I quit the component.
In the firebase documentation indicates that you must use the unsubscribe() function; to stop listening to the collection.
Unfortunately, this function cannot be used directly because it is declared undefined.
Here is the component code:
<script>
import db from "../../firebase/init";
let subscribe;
export default {
// ...
beforeDestroy() {
// Don't work form me !!!
unsubscribe();
},
methods: {
async getMyCollection() {
try {
subscribe = await db.collection("myCollection");
subscribe.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
// Do something
}
});
});
} catch (error) {
console.log(error);
}
}
}
</script>
thanks for the help

Its because you have not defined the unsubscribe anywhere. Please check the code below.
<script>
import db from "../../firebase/init";
let unsubscribe;
export default {
// ...
beforeDestroy() {
unsubscribe();
},
methods: {
async getMyCollection() {
try {
unsubscribe = await db.collection("myCollection")
.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
// Do something
}
});
});
} catch (error) {
console.log(error);
}
}
}
</script>

Related

#aws-amplify/ui-components - Trigger Toast with Custom Message

I'm using #aws-amplify/ui-components library which is deprecated (for Vue 2). It comes with a Toast component (<amplfy-toast />)which alerts error messages in the top of the screen. In my Vue js component I want to trigger message but programatically where the console.log() is:
watch: {
authState: {
async handler(state) {
const data = this.authData
if (state === 'signedin') {
try {
const local = await this.axios.post('/api/v1/authentication/login', {
token: data.getSignInUserSession().getIdToken().getJwtToken()
})
if ( ! local.data.error) {
await this.$store.dispatch('login', data)
}
} catch (error) {
console.log(error) // Trigger toast here.
}
}
},
immediate: true
}
},
How can I do this?
You can do this with dispatchToastHubEvent() function or simply call:
Hub.dispatch('UI Auth', {
event: 'ToastAuthError',
message: error.message
})

nuxt class component. async fetch dont work

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
#Component
export default class Index extends Vue {
form = {
name: '',
adress: '',
age: '',
items: [],
}
async fetch() {
this.form.items = await fetch('https://api.nuxtjs.dev/posts').then(c => c.json())
console.log(this.form.items)
}
fetchOnServer = true;
changeLanguage(language: string) {
this.$i18n.setLocale(language);
}
}
</script>
async fetch dont work in class component
If I use a standard component it works fine. But if I use a class component, the function call stops working.
What is the problem and how can it be fixed
As an annotation argument it works
#Component({
components: {ValidationProvider, ValidationObserver},
async fetch() {
console.log('** fetch at ');
console.log(this.$route);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
}
})
export default class xrayClient extends Vue {
As class methods created() works:
async created() {
console.log('** created ');
console.log(this.$route);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
Mounted() works:
async mounted() {
console.log('** mounted ' + this.data);
await new Promise((resolve) => {
setTimeout(resolve, 1000);
});
}
Calling
async asyncData()
and
fetch() {
Don't run.
"nuxt": "^2.15.7",
Instead of vue-property-decorator, you should use nuxt-property-decorator.
Then you can specify your asyncData or fetch methods in the component options, like this:
<script lang="ts">
import { Vue, Component } from 'nuxt-property-decorator'
#Component({
async fetch() {
this.form.items = await fetch('https://api.nuxtjs.dev/posts').then(c => c.json())
console.log(this.form.items)
}
})
export default class Index extends Vue {
form = {
name: '',
adress: '',
age: '',
items: [],
}
changeLanguage(language: string) {
this.$i18n.setLocale(language);
}
}
</script>
See this GitHub issue comment for details: https://github.com/nuxt/nuxt.js/issues/5330#issuecomment-475595112

How to test router code that contains heavy logic using sinon and stubbing (nodeJS)

I am new to using sinon, so sorry if my question is weird, I looked everywhere but can't find a way to do it.
I have app with express router. I want to write uint test for one of the routes. That route have an inner function that is 'heavy', meaning that it is async with promise, and in reality calls an external api. I want to stub that inner function in the test so that it will not use the api, and will return my own data instead of the original method.
This is the code so far:
routes/setOrder.js:
// the inner function I want to stub
var verifyPayment = function(saleId) {
return new Promise((resolve, reject) => {
logger.info(`verifyPayment: ${saleId}`);
externalAPICall.get( // <==this is the 'heavey part!!
saleId,
function (error, sale) {
if(error) {
return reject(`Error querying sale(${saleId}): ${error}`);
}
resolve(sale);
});
});
}
router.get('/paymentId/:paymentId', setOrderWithGet);
const setOrderWithGet =async function(req, res, next) {
const { paymentId } = req.params;
verifyPayment(paymentId)
.then(async sale => {
try {
console.log(`sale:${sale}`);
res.send(JSON.stringify({"status": "ok!" }));
} catch (err) {
logger.warn(err)
res.send(JSON.stringify({"status": "fail.."}));
}
})
.catch(reason => {
logger.warn(`[] Payment(${paymentId}) is not valid ${reason}`);
res.send(JSON.stringify({"status": "fail.."}));
});
}
module.exports = router;
module.exports.setOrderWithGet = setOrderWithGet;
module.exports.verifyPayment = verifyPayment;
setOrderTest.js:
const setOrderStub = require('../routes/setOrder');
describe("POST /setOrder", () => {
beforeEach(() => {
sinon.stub(setOrderStub, 'verifyPayment').resolves({....});
});
afterEach(() => {
sinon.restore();
});
describe("test1", () => {
it("setOrder first attempt", () => {
let req ={params : {'paymentId' : 'mypamentid1'}};
setOrderStub.setOrderWithGet(req,{});
});
});
});
This line:
sinon.stub(setOrderStub, 'verifyPayment').resolves({....});
...stubs the verifyPayment function on the module exports of the setOrder module.
Right now setOrderWithGet is calling the verifyPayment function directly, so it is unaffected by any changes to the module exports.
Change setOrderWithGet to call verifyPayment using the module exports:
const setOrderWithGet = async function(req, res, next) {
// ...
module.exports.verifyPayment(paymentId) // <= call the module export for verifyPayment
// ...
}
...and your stub will get called.

Vuejs reset fields after POST success callback

I'm having some problems resetting a textarea field after a POST request.
This is my component code
<template lang="pug">
.col-sm-12
h2 Add new Task
hr
.form-group
textarea.form-control(v-model="task.taskContent")
.form-group
button.btn.btn-primary(#click="createNewTask") Add Task
</template>
<script>
export default {
data() {
return {
task: {
taskContent: ''
}
};
},
methods: {
createNewTask() {
if (this.task.taskContent.length > 0) {
// Sending data to the server
this.$http.post('https://vue-taskmanager.firebaseio.com/task.json', this.task)
.then(response => {
console.log(response);
// Adding the new task to the main template list
this.$emit('taskWasCreated', this.task);
// Resetting textarea content
this.task.taskContent = '';
}, error => {
console.log(error);
});
} else {
alert("Sorry you can't create an empty task");
}
}
}
}
</script>
This is the parent component
<template lang="pug">
.container
.row
app-newtask(#taskWasCreated="addTask")
app-taskswrapper(:tasks="tasksArr")
app-footer
</template>
<script>
import { EventBus } from './main.js';
import UserRegistration from './components/user/UserRegistration.vue';
import TasksWrapper from './components/TasksWrapper.vue';
import NewTask from './components/NewTask.vue';
import Footer from './components/Footer.vue';
export default {
data() {
return {
tasksArr: [
'Just something to see'
]
};
},
methods: {
addTask(task) {
this.tasksArr.push(task)
}
},
// Listening on Events from Task.vue
created() {
// Delete task from array
EventBus.$on('taskWasDeleted', (taskIndex) => {
this.tasksArr.splice(taskIndex, 1);
// Delete task from db
this.$http.delete('https://vue-taskmanager.firebaseio.com/task.json', this.task)
.then(response => {
console.log(response);
}, error => {
console.log(error);
});
});
// Fetch tasks from db
this.$http.get('https://vue-taskmanager.firebaseio.com/task.json')
.then(response => {
return response.json();
})
.then(task => {
const resultsArray = [];
for (let key in task) {
resultsArray.push(task[key]);
}
this.tasksArr = resultsArray;
});
},
components: {
'app-taskswrapper': TasksWrapper,
'app-newtask': NewTask,
'app-footer': Footer,
'app-userregistration': UserRegistration
}
}
</script>
As you can see inside the response callback function I reset the task.taskContent value but the problem is that the string is sent to the db without problems while is not updated in the root component where I have an array storing all these strings.
I was thinking about using a watcher but I don't know if it's a good solution, do you have any suggestions?
Link to the github repo https://github.com/Polenj86/vue-taskmanager
It's clear what is happening now that you've posted your parent component.
You are storing the task object in the parent's array. This is not going to be a copy of the task, it's going to be a reference of the same task that you are about to clear. So when you later set this.task.taskContent = '' you are changing the task in the parent array too.
Consider this:
var task_holder_array = []
var task = {name: "mark"}
task_holder_array.push(task)
console.log("array before: ", task_holder_array)
task.name = ""
console.log("array after: ", task_holder_array)
You need to somehow create a new task object to push into the parent's array. There are a lot of ways you could do this. For example:
this.$emit('taskWasCreated', {name: this.task.name});
Or you could just pass the task name string to the parent and let the parent create the object.

How can I use Ionic 2 Pulling Refresher in my app?

Hi all I'M working on angularjs 2/ionic2 mobile app , i need to do pulling refresher in my app, we have tried this link:- https://ionicframework.com/docs/v2/api/components/refresher/Refresher/ process i got refreshing the page but it's not get dismissLoader, we have given the images of my app refreshing:-
we don't know where we did the mistake and where we need to add the correct functionality in my project...
while we pulling the page it's refreshing but it's not get dismiss, Refreshing text and icon showing it's not get dismissed...
what we expecting once we pulled the page it's need to refresh after that refreshing text and icon need to be dismiss...
**we added coding only in html:-****
<ion-refresher (ionRefresh)="setFilteredItems($event)">
<ion-refresher-content refreshingSpinner="circles" refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>
we have not added anything in type script part...so please check and update the solution please....
we have created example Plunker
please update the plunker as well to know the solution, thanks.....
My Type Script constructor code:-
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { GlobalStateService } from '../../services/global-state.service';
import { AccountSigninPage } from '../account-signin/account-signin';
import { AccountSignupPage } from '../account-signup/account-signup';
import { ActivityAddPage } from '../activity-add/activity-add';
import { Activity } from "../../services/actopi-sdk/model/Activity";
import { UserLoginService } from "../../services/account-management.service";
import { ResourceListPage } from '../resource-list/resource-list';
import { IamAuthorizerClient } from "../../services/actopi-api.service";
import { CustomAuthorizerClient, NoAuthorizationClient, UserPoolsAuthorizerClient } from "../../services/actopi-api.service";
import { Config } from '../../config/config'
import { Logger } from '../../services/logger.service';
import 'rxjs/add/operator/debounceTime';
import { FormControl } from '#angular/forms';
declare const AWS: any;
#Component({
templateUrl: 'activity-list.html',
})
export class ActivityListPage {
initialized = false;
accountSigninPage = AccountSigninPage;
accountSignupPage = AccountSignupPage;
activityAddPage = ActivityAddPage;
activitys: Activity[] = [];
resourceListPage = ResourceListPage;
searchTerm: string = '';
searchControl: FormControl;
displayDeleteActivityConfirmation(activityId, activityName) {
console.log("Deleting activityID " + activityId);
let confirm = this.globals.getAlertController().create({
title: 'Delete activity?',
message: `Are you sure you want to delete [<b>${activityName}</b>]? All resources and bookings associated with [<b>${activityName}</b>] will also be deleted!`,
buttons: [
{
text: 'Cancel',
handler: () => { /* do nothing */ }
},
{
text: 'OK',
handler: () => {
this.deleteActivity(activityId)
.then(() => {
this.globals.dismissLoader();
this.globals.displayToast(`Activity [${activityName}] has been successfully deleted`);
})
.catch((err) => {
this.globals.dismissLoader();
this.globals.displayAlert('Error encountered',
'Delete failed. Please check the console logs for more information.');
console.log(err);
});
}
}
]
});
confirm.present();
}
deleteActivity(activityId): Promise<void> {
return new Promise<void>((resolve, reject) => {
// delete from the database
this.globals.displayLoader("Deleting...");
this.customAuthClient.getClient().activitysDelete(activityId).subscribe(
() => {
// remove the item from the activitys array
let index = this.activitys.findIndex(activity => { return activity.activityId == activityId });
if (index > -1) {
this.activitys.splice(index, 1);
}
resolve();
},
(err) => {
reject(err);
}
);
});
}
gotoResourceListPage(activity) {
this.navCtrl.push(ResourceListPage, activity);
}
filterItems(searchTerm): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
this.activitys = data.items.filter((activity) => {
return activity.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
});
}
loadActivitysWithAuth(): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
let searchTerm: string = '';
// this.activitys = data.items.sort((a, b) => {
// return a.name.localeCompare(b.name);
// });
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
loadActivitysWithoutAuth(): void {
this.activitys = [];
this.noAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
this.activitys = data.items.sort((a, b) => {
return a.name.localeCompare(b.name);
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
constructor(private navCtrl: NavController, public globals: GlobalStateService, private noAuthClient: NoAuthorizationClient, private customAuthClient: CustomAuthorizerClient, private userPoolsAuthClient: UserPoolsAuthorizerClient, private authClient: IamAuthorizerClient) {
this.searchControl = new FormControl();
}
ionViewDidEnter() {
Logger.banner("Activitys");
this.activitys = [];
if (!this.initialized) {
this.initialized = false;
if (UserLoginService.getAwsAccessKey() != null) {
// if (CognitoUtil.getUserState() === UserState.SignedIn) {
// console.log(AWS.config.credentials);
UserLoginService.getAwsCredentials()
.then((data) => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.searchControl.valueChanges.debounceTime(700).subscribe(search => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.globals.dismissLoader();
});
})
.catch((err) => {
console.log("ERROR: Unable to load activitys!");
console.log(err)
})
}
}
}
setFilteredItems(refresher) {
return this.filterItems(this.searchTerm);
refresher.complete();
}
}
You need to call refresher.complete() to dismiss your refresher once loading of new data is done..
setFilteredItems(refresher?:any){
//async call to load.
// in the then function
if(refresher)
refresher.complete();
}
}
The refresher is sent from the html onRefresh. With ? you can call without passing object in your code.
this.setFilteredItems();
Also consider refactoring your code. You should ideally call complete after async task ia done and no point in returning another function to the html side and calling complete after return will just end up as dead code.

Resources