Create Radio Button Alert from Firestore Data - ionic-framework

I want to create a radio button alert by using the data in fireStore. I generated an observable by valueChanges() but console.log returns Undefined when I used it in the function that can't read the data and eventually cannot insert the values for radio button. I am new to fireStore and ionic.
I have also tried using .get().then(function(doc) but returns error as not a function. I have also tried using subscribe() but also not able to give me the actual data, or I have missed something. I have google for many days but just can't find the solution. I hope somebody could help.
myMemberList = [];
constructor(public navCtrl: NavController,
public alertCtrl: AlertController,
public firestore: AngularFirestore,
public afAuth: AngularFireAuth,
) { }
ionViewDidEnter() {
this.afAuth.authState.subscribe(user => {
if (user) {
this.userId = user.uid;
this.fireStoreTaskList = this.firestore.doc<any>('users/' +
this.userId).collection('Member').valueChanges();
}
});
}
// create the inputs for radio button //
createInputs() {
const theNewInputs = [];
for (let i = 0; i < this.fireStoreTaskList.length; i++) { // undefined
theNewInputs.push(
{
type: 'radio',
label: this.fireStoreTaskList.memberName, // undefined
value: this.fireStoreTaskList.memberId, // undefined
checked: false
}
);
} {
console.log(theNewInputs);
}
return theNewBeneInputs;
}
// Radio button alert to choose data //
async selectMember() {
this.myMemberList = this.createInputs();
const alert = await this.alertCtrl.create({
header: 'Member',
inputs: this.myMemberList,
buttons: [{ text: 'Cancel', role: 'cancel' },
{ text: 'OK',
handler: data => {
console.log(data)
}
}
]
});
await alert.present();
}

I have been working with Ionic 4 for some time now and I have also integrated Firebase Firestore in my app. I didn't really understand the whole description, but I have a solution for you initial question "I want to create a radio button alert by using the data in Firestore"
I assume that you have already setup your application with your Firebase app, if not then I suggest following the How to Build An Ionic 4 App with Firebase and AngularFire 5.
My example has 1 button, that whenever you click it, it will do the following:
Access the Firestore database.
Download the Firestore documents.
Get the field memberName of each document.
Add those names in an array of names
Create an Alert of Radio Buttons.
For the radio buttons it will create a list of radio buttons that will have the names of the members.
Display the array.
For my code to work, this is the Firestore database structure that I have followed:
.
└── collection: "users"
└── document: "autogenerated_id"
| ├── memberID: "user_id"
| └── memberName: "Name 01"
└── document: "autogenerated_id"
├── memberID: "user_id"
└── memberName: "Name 02"
When clicking the button you will see an alert with radio buttons e.g. Name 01 and Name 02
As I have mentioned above, this is my example code. It loads data from Firestore and Creates an alert with radio buttons using that data, as you have described in your question. I have added a lot of comments for you in the code. If this is not exactly what you were looking for, take a look at the code and modify it according to your needs.

UPDATED CODE FOR LOADING memberID and memberName into the array of type interface.
import { Component, OnInit } from '#angular/core';
//Import AngularFirestore to access Firestore database
import { AngularFirestore } from '#angular/fire/firestore';
//Import AlertControll to display alerts
import { AlertController } from '#ionic/angular';
//You have to add the interface, before the calss' declaration
interface Data {
memberID?: string;
memberName?: string;
}
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
//List that will be used to load the data from Firestore into it
//members = []; //You don't this anymore
//Just the array of type interface that you created.
data: Data[] = [];
//Constractor: add AngularFirestore and AlertController
constructor(private db: AngularFirestore, public alertController: AlertController) { }
ngOnInit() {
}
//Load data and create the alert
showAlert(){
//Clear the array before loading again
this.members = [];
//Access the Collection "users" in Firestore and load all the documents
this.db.collection("users").ref
.get()
.then(async (querySnapshot) => {
//Parse through all the loaded documents
querySnapshot.forEach((doc) => {
//Add the loaded name to the list
//this.members.push(doc.data().memberName) // You don't need this anymore as you are going to push the loaded data in the new array
//Pushing the data in the new array or logging it if you want
this.data.push( {
memberID: doc.data().memberID, //This is how you get the memberID from Firestore document
memberName: doc.data().memberName} //This is how you get the memberName from Firestore document
);
});
//Create an array of Radio Buttons to be used in the alert
var newInputs = [];
//Parse through all memebers in the loaded array from Firestore
for (const d of this.data){
newInputs.push({
name: 'radio1', //You can costumize those as well to cast the clicked once afterwards
type: 'radio',
label: "ID: " + d.memberID + " name: " + d.memberName, //Add the member and the ID as label
value: 'value1',
checked: false
})
}
//Create an alert
const alert = await this.alertController.create({
header: 'Radio',
inputs: newInputs, //Add the dynamically generated array of radio buttons.
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary',
handler: () => {
console.log('Confirm Cancel');
}
}, {
text: 'Ok',
handler: () => {
console.log('Confirm Ok');
}
}
]
});
//Present the alert
await alert.present();
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
}

Related

ionic push notification when app is in foreground

I am making a ionic 3 app. I want notifications to appear even when app is in foreground. I have tried using FCM Plugin I'm getting notifications only when app is in background.
Home.ts
import { AngularFireDatabase } from 'angularfire2/database';
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import firebase from 'firebase';
declare var FCMPlugin;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
firestore = firebase.database().ref('/pushtokens');
firemsg = firebase.database().ref('/messages');
constructor(public navCtrl: NavController,public afd:AngularFireDatabase) {
this.tokensetup().then((token)=>{
this.storeToken(token);
})
}
ionViewDidLoad() {
FCMPlugin.onNotification(function (data) {
if (data.wasTapped) {
//Notification was received on device tray and tapped by the user.
alert(JSON.stringify(data));
} else {
//Notification was received in foreground. Maybe the user needs to be notified.
alert(JSON.stringify(data));
}
});
FCMPlugin.onTokenRefresh(function (token) {
alert(token);
});
}
tokensetup(){
var promise = new Promise((resolve,reject)=>{
FCMPlugin.getToken(function(token){
resolve(token);
},(err)=>{
reject(err);
});
})
return promise;
}
storeToken(token){
this.afd.list(this.firestore).push({
uid: firebase.auth().currentUser.uid,
devtoken: token
}).then(()=>{
alert('Token stored')
}).catch(()=>{
alert('Token not stored');
})
// this.afd.list(this.firemsg).push({
// sendername:'adirzoari',
// message: 'hello for checking'
// }).then(()=>{
// alert('Message stored');
// }).catch(()=>{
// alert('message not stored');
// })
}
}
the function cloud for notifications
var functions = require('firebase-functions');
var admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var wrotedata;
exports.Pushtrigger = functions.database.ref('/messages/{messageId}').onWrite((event) => {
wrotedata = event.data.val();
admin.database().ref('/pushtokens').orderByChild('uid').once('value').then((alltokens) => {
var rawtokens = alltokens.val();
var tokens = [];
processtokens(rawtokens).then((processedtokens) => {
for (var token of processedtokens) {
tokens.push(token.devtoken);
}
var payload = {
"notification":{
"title":"From" + wrotedata.sendername,
"body":"Msg" + wrotedata.message,
"sound":"default",
},
"data":{
"sendername":wrotedata.sendername,
"message":wrotedata.message
}
}
return admin.messaging().sendToDevice(tokens, payload).then((response) => {
console.log('Pushed notifications');
}).catch((err) => {
console.log(err);
})
})
})
})
function processtokens(rawtokens) {
var promise = new Promise((resolve, reject) => {
var processedtokens = []
for (var token in rawtokens) {
processedtokens.push(rawtokens[token]);
}
resolve(processedtokens);
})
return promise;
}
it works only when the app in the background. but when i exit from the app and it's not in the background I don't get any notification.
You need to edit the FCM Plugin files. I found the solution only for android now.
I use https://github.com/fechanique/cordova-plugin-fcm this FCM plugin for android and ios in cordova.
You need to edit file MyFirebaseMessagingService.java line 53(line no be may be differ).
In this file there is a method onMessageReceived at the end of the method there is a line which is commented, this line calling an another method i.e. sendNotification(....).
sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody(), data);
You have to uncomment this line and change last parameter from remoteMessage.getData() to data (data variable is already there in the code).
And comment this line FCMPlugin.sendPushPayload( data );
Now you are good to go. Now you are able to receive notification even when app is opened (foreground), you will receive the banner (floating) notifications.
If you found anything for IOS please let me know!!!
I am using firebase plugin for ionic 3.
There is a check if notification data contain "notification_foreground" or not and save it in variable foregroundNotification.
if(data.containsKey("notification_foreground")){
foregroundNotification = true;
}
then it create showNotification variable which decide if we need to show notification or not and pass this to the sendMessage (show notification function).
if (!TextUtils.isEmpty(body) || !TextUtils.isEmpty(title) || (data != null && !data.isEmpty())) {
boolean showNotification = (FirebasePlugin.inBackground() || !FirebasePlugin.hasNotificationsCallback() || foregroundNotification) && (!TextUtils.isEmpty(body) || !TextUtils.isEmpty(title));
sendMessage(data, messageType, id, title, body, showNotification, sound, vibrate, light, color, icon, channelId, priority, visibility);
}
your payload should contain notification_foreground, notification_title and notification_body.

one signal additional data in ionic 2/3

I'm trying to work with one signal plugin in my ionic 2 app
I've installed Onesignal and it was working fine,but i don't know how to work with handleNotificationOpened function
there is no document at all (nothing was found)
this is my code:
this.oneSignal.handleNotificationReceived().subscribe((msg) => {
// o something when notification is received
});
but I have no idea how to use msg for getting data.
any help? link?
tank you
Here is how i redirect user to related page when app launch from notification.
app.component.ts
this.oneSignal.handleNotificationOpened().subscribe((data) => {
let payload = data; // getting id and action in additionalData.
this.redirectToPage(payload);
});
redirectToPage(data) {
let type
try {
type = data.notification.payload.additionalData.type;
} catch (e) {
console.warn(e);
}
switch (type) {
case 'Followers': {
this.navController.push(UserProfilePage, { userId: data.notification.payload.additionalData.uid });
break;
} case 'comment': {
this.navController.push(CommentsPage, { id: data.notification.payload.additionalData.pid })
break;
}
}
}
A better solution would be to reset the current nav stack and recreate it. Why?
Lets see this scenario:
TodosPage (rootPage) -> TodoPage (push) -> CommentsPage (push)
If you go directly to CommentsPage the "go back" button won't work as expected (its gone or redirect you to... who knows where :D).
So this is my proposal:
this.oneSignal.handleNotificationOpened().subscribe((data) => {
// Service to create new navigation stack
this.navigationService.createNav(data);
});
navigation.service.ts
import {Injectable} from '#angular/core';
import {App} from 'ionic-angular';
import {TodosPage} from '../pages/todos/todos';
import {TodoPage} from '../pages/todo/todo';
import {CommentsPage} from '../pages/comments/comments';
#Injectable()
export class NavigationService {
pagesToPush: Array<any>;
constructor(public app: App) {
}
// Function to create nav stack
createNav(data: any) {
this.pagesToPush = [];
// Customize for different push notifications
// Setting up navigation for new comments on TodoPage
if (data.notification.payload.additionalData.type === 'NEW_TODO_COMMENT') {
this.pagesToPush.push({
page: TodoPage,
params: {
todoId: data.notification.payload.additionalData.todoId
}
});
this.pagesToPush.push({
page: CommentsPage,
params: {
todoId: data.notification.payload.additionalData.todoId,
}
});
}
// We need to reset current stack
this.app.getRootNav().setRoot(TodosPage).then(() => {
// Inserts an array of components into the nav stack at the specified index
this.app.getRootNav().insertPages(this.app.getRootNav().length(), this.pagesToPush);
});
}
}
I hope it helps :)

Angular2 dynamic form with remote metadata

I created a dynamic form following the instructions in the angular cookbook and then I've tried to create the form with metadata that I have in my database.
I made an HTTP request to the get field types, names, ids, etc. but when I try to build the form as in the angular example, nothing happens or I get errors on console.
Here's the code from the tutorial:
export class AppComponent {
questions: any[];
constructor(service: QuestionService) {
this.questions = service.getQuestions();
}
}
And this is what I did:
export class AppComponent implements OnInit {
campos: any[] = [];
constructor(private servico: FormDadosService) {}
ngOnInit() {
this.servico.getCampos().subscribe(this.processaCampos);
}
processaCampos(dados) {
for (let i = 0; i < dados.length; i++) {
this.campos.push(new CampoBase({
nome: dados[i].ZI2_NOME,
label: dados[i].ZI2_DESC,
ordem: dados[i].ZI2_ORDEM,
obrigatorio: dados[i].ZI2_OBRIGAT,
tamanho: dados[i].ZI2_TAM,
valor: '',
tipoCampo: dados[i].ZI2_TIPO
}))
}
}
}
I am getting this error:
error_handler.js:50EXCEPTION: Cannot read property 'push' of undefined
I think I need to know a way to render the form after all data about it has arrived from my HTTP request.
I made it work this way:
export class AppComponent implements OnInit {
campos: any[] = [];
constructor(private servico: FormDadosService) { }
ngOnInit() {
this.servico.getCampos().subscribe((data) => {
data.forEach(campo => {
this.campos.push(new CampoBase({
valor: '',
nome: campo.ZI2_CAMPO,
label: campo.ZI2_DESC,
tipoCampo: campo.ZI2_TIPO,
tamanho: campo.ZI2_TAM
}))
});
});
}
}
This question can be marked as solved.
Thanks everyone.

ionic 2: how to dismiss loader after data is ready?

In my Ionic 2 app, I have i have component that GET to fetch data.
i want to maker loader and dismiss it after data is ready.
i tried to look on other posts around the stack overflow but my issue is different.
i did something but the loader is forever and its not helps me.
It looks like following:
import { Component,ViewChild } from '#angular/core';
import { NavController,LoadingController,AlertController,ViewController} from 'ionic-angular';
import { Facebook } from 'ionic-native';
//import pages
import {LoginPage} from "../../pages/login/login";
import {User} from '../../models/user'
import { Storage} from '#ionic/storage';
//import provider
import { ProfileData } from '../../providers/profile-data';
import { NotesData } from '../../providers/notes-data';
import firebase from 'firebase'
import {AddNote} from "../add-note/add-note";
/*
Generated class for the NotesList page.
See http://ionicframework.com/docs/v2/components/#navigation for more info on
Ionic pages and navigation.
*/
#Component({
selector: 'page-notes-list',
templateUrl: 'notes-list.html'
})
export class NotesList {
//facebook user
userProfile: any = null;
uid: any = null;
fireUid:any=null;
name:string=null;
photo: any =null;
user:User=null;
photos:any=null;
currentUser:any=null;
photonew:any=null;
//notes list
notes:any=null;
data:any;
pages: Array<{title: string, component: any}>;
constructor(public navCtrl: NavController,public profileData:ProfileData,private viewCtrl: ViewController,public notesData:NotesData,private loadingCtrl: LoadingController,private alertCtrl: AlertController,public storage:Storage) {
this.data={};
this.data.title="";
this.data.desc="";
}
ionViewDidLoad() {
//if i do that the loader is forever
/*
let loader = this.loadingCtrl.create({
dismissOnPageChange: true,
});
loader.present();
*/
// here i want the loader to be until the data is ready.
this.getNotesList(); //this functions not returns data so i can't do this.getNotesList().then(()=>
}
getNotesList(){
console.log("get event");
var that=this;
this.notesData.getNotesLIst().on('value', snapshot => {
let notesList= [];
snapshot.forEach( snap => {
console.log("id note"+snap.val().id);
notesList.push({
id: snap.val().id,
title: snap.val().title,
desc: snap.val().desc,
color:snap.val().color,
photo:snap.val().photo,
});
});
that.notes = notesList;
});
}
addNote(){
this.navCtrl.push(AddNote);
}
logOutFacebook(){
Facebook.logout().then((response)=>
{
this.navCtrl.push(LoginPage);
alert(JSON.stringify(response));
},(error)=>{
alert(error);
})
}
}
At first, you should show how do you implement your loading page. Is it a splash screen with cordorva? Or just as div displaying some image?
If it is a splash screen, you can add this code in your component after you get data, (it is from starter template, you can see the detail by creating a new project with ionic start):
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
Splashscreen.hide();
});
And, if you use a div in your index page, it is similar, you can just get that element and remove it, with pure js.
okay it succeed to do that
this is my answer
ionViewDidLoad() {
let loader = this.loadingCtrl.create({});
loader.present();
this.getNotesList().then((x) => {
if (x) loader.dismiss();
});
}
getNotesList(){
return new Promise(resolve => {
var that=this;
this.notesData.getNotesLIst().on('value', snapshot => {
let notesList= [];
snapshot.forEach( snap => {
console.log("id note"+snap.val().id);
notesList.push({
id: snap.val().id,
title: snap.val().title,
desc: snap.val().desc,
color:snap.val().color,
photo:snap.val().photo,
});
});
that.notes = notesList;
resolve(true);
});
})
}

Ionic2 alert with dropdown?

I am building an Ionic2 app. I have an alert like following:
constructor(private platform: Platform, public nav : NavController,
public exhibitionSurveyObjectService : ExhibitionSurveyObjectService ) {
this.initializeMap();
this.nav=nav;
this.testArray=[];
this.area=null;
}
addSurveyObject(){
let prompt = Alert.create({
title: 'Subscribe to our service',
message: "All the fields are necessary",
inputs: [
{
name: 'name',
placeholder: 'Name'
},
....
{
name: 'cycle',
placeholder: 'Cycle: once/weekly/monthly'
},
{
name: 'object_type',
placeholder: 'Farm/Solarpanel/plain'
},
],
buttons: [
....
{
text: 'Save',
handler: data => {
this.createExhibitionSuveyObject(data);
}
}
]
});
this.nav.present(prompt);
}
createExhibitionSuveyObject(data: any){
var cycle = data.cycle;
cycle = cycle.toUpperCase()
console.log(cycle)
var type = data.object_type;
type = type.toUpperCase()
console.log(type)
this.exhibitionSurveyObjectService.addObject(
data.name, data.farmer_email,
data.farmer_name, data.size, data.path, cycle, type).subscribe(
response => {
this.exhibitionSurveyObjects = response;
this.sayThanks();
},
error => {
this.errorMessage = <any>error;
console.log("error")
}
);
}
sayThanks(){
let alert = Alert.create({
title: 'Thank you!',
subTitle: 'We have received your data, we will get back to you soon!',
buttons: [{
text: 'Ok',
handler: () => {
this.nav.push(HomePage)
}
}]
});
this.nav.present(alert);
}
I want the last two fields to be dropdowns. How can I achieve this?
UPDATE: updated the code snippet with some more code. How it can be updated to use Modal instead of alert?
Just like you can see in Ionic2 docs
Alerts can also include several different inputs whose data can be
passed back to the app. Inputs can be used as a simple way to prompt
users for information. Radios, checkboxes and text inputs are all
accepted, but they cannot be mixed. For example, an alert could have
all radio button inputs, or all checkbox inputs, but the same alert
cannot mix radio and checkbox inputs.
And...
If you require a complex form UI which doesn't fit within the
guidelines of an alert then we recommend building the form within a
modal instead.
So you'll have to create a new Component with that form and then use it to create the Modal:
import { Modal, NavController, NavParams } from 'ionic-angular';
#Component(...)
class YourPage {
constructor(nav: NavController) {
this.nav = nav;
}
presentSubscriptionModal() {
let subscriptionModal = Modal.create(Subscription, { yourParam: paramValue });
this.nav.present(subscriptionModal);
}
}
#Component(...)
class Subscription{
constructor(params: NavParams) {
let param = params.get('yourParam');
}
}