Ionic 3 - Sending data to service from component - ionic-framework

I am trying to send an object from my component which is from the input of the user to the service I have.
This is my code..
Sample.html
<ion-item>
<ion-label color="primary">Subject</ion-label>
<ion-select [(ngModel)]="attendanceSet.subject">
<ion-option *ngFor="let subject of subjects" [value]="subject.title">{{subject.title}}</ion-option>
</ion-select>
</ion-item>
<ion-item>
<ion-label color="primary">Date</ion-label>
<ion-datetime [(ngModel)]="attendanceSet.date" displayFormat="DDD MMM DD" pickerFormat="YYYY MM DD"></ion-datetime>
</ion-item>
<button ion-button block padding (click)="proceed(attendanceSet)">Proceed</button>
Sample.ts
public attendanceSet = {}
proceed(attendanceSet) {
this.navCtrl.push(CheckAttendancePage, {
setData: attendanceSet
});
}
Service
private attendanceListRef: AngularFireList<Attendance>
public setAttendance = {}
constructor(private db: AngularFireDatabase, private events: Events, public navParams: NavParams, private method: CheckAttendancePage){
this.attendanceListRef = this.db.list<Attendance>('attendance/')
}
addAttendance(attendance: Attendance) {
return this.attendanceListRef.push(attendance)
}
What I'm trying to do is getting data from the sample.html which I will later use to define the path of the "attendanceListRef" like this "attendance/subject/date"
Is there any proper or alternative way to do this? Thanks!

It's quite simple to send data to a Service with Angular. I'll give you three options in this example.
service.ts
import { Injectable } from '#angular/core';
import { Events } from 'ionic-angular';
#Injectable()
export class MyService {
// option 1: Using the variable directly
public attendanceSet: any;
// option 3: Using Ionic's Events
constructor(events: Events) {
this.events.subscribe('set:changed', set => {
this.attendanceSet = set;
});
}
// option 2: Using a setter
setAttendanceSet(set: any) {
this.attendanceSet = set;
}
}
In your Component you can do the following.
import { Component } from '#angular/core';
import { Events } from 'ionic-angular';
// import your service
import { MyService } from '../../services/my-service.ts';
#Component({
selector: 'something',
templateUrl: 'something.html',
providers: [MyService] // ADD HERE -> Also add in App.module.ts
})
export class Component {
newAttendanceSet = {some Object};
// Inject the service in your component
constructor(private myService: MyService) { }
proceed() {
// option 1
this.myService.attendanceSet = this.newAttendanceSet;
// option 2
this.myService.setAttendanceSet(this.newAttendanceSet);
// option 3
this.events.publish('set:changed', this.newAttendanceSet);
}
}
Basically I'd recommend option 1 or 2. 3 is considered bad practice in my eyes but it still works and sometimes allows for great flexibility if you need to update your value over multiple services/components.

I tried to save the attendanceSet on an event then add listener on the service but is not successful. It seems like events do not work on services? Cause when I added the listener to a different component is worked but not on the service.

Related

How can I add pagination on my ionic app?

I am building a mobile app from my WordPress website I am getting the content from my WordPress website but I don't know how to load more data to the page
Here is my html code:
<ion-list>
<ion-item *ngFor="let item of getData">{{item}}</ion-item>
</ion-list>
<ion-infinite-scroll (ionInfinite)="doInfinite($event)">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
</ion-list>
Here is my ts file:
import { Component } from '#angular/core';
import { NavController, NavParams, Item } from 'ionic- angular';
import { Api, Items } from '../../providers';
import { HttpClient } from '#angular/common/http';
#IonicPage()
#Component({
selector: 'page-latest',
templateUrl: 'latest.html',
})
export class LatestPage {
currentItems: Item[];
item: any;
getData: Object;
constructor(public navCtrl: NavController, toastCtrl: ToastController,
public api:Api, navParams: NavParams, items: Items,public
http:HttpClient)
{
this.api.getPosts().subscribe(data=>{
console.log(data)
this.getData = data
})
}
doInfinite(infiniteScroll: any) {
this.api.getPosts().subscribe(data=>{
console.log(data)
this.getData = data
})
infiniteScroll.complete()
}
openItem(item){
this.navCtrl.push('ItemDetailPage', {
itemName: item
});
}
}
I need the data to be displayed when we scroll down.
Pagination doesn't really work on a mobile app. The popular replacement for that, is to use an inifnite scroll.
It works by adding a HTML element on your display. You first fetch a batch of records to display ( let's say 50 ) & when the user reaches to the end of these records, infinite scroll gets triggered, you fetch the next 50 records from the server & display.
The fetching of data is done by adding the parameters limit & offset to your api call.
data; //data to display
getData(limit, offset) {
this.http.get(API_URL"+&limit="+limit+"&offset="+offset,{}).subscribe((response) =>{
this.data.push(response);
}
}
doInfinite(infiniteScroll) {
//Calculate your limit & offset values
this.getData(this.limit, this.offset);
infiniteScroll.complete();
}
This tutorial might be helpful for your particular case.

Displaying component property from record provider is undefined

I am creating an ionic app. In this modal, I want a select with options populated from my Provider (called recordProvider). categories should hold an array of objects from the recordProvider.
The name property of these objects is what goes in the select.
I am able to log categories immediately after it is assigned from recordsProvider and it shows all the proper records perfectly. However, the next line logs the length at 0. Most importantly, the UI errors with "Cannot read property 'name' of undefined"
Why does categories have this inconsistent value?
If it is just an issue of timing and categories will have the correct data in a moment, why isn't it updated in the UI? Isn't that the whole get with Angular?
How do I fix it?
Modal ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams,ViewController } from 'ionic- angular';
import { RecordsProvider } from './../../providers/records/records';
#IonicPage()
#Component({
selector: 'page-add-modal',
templateUrl: 'add-modal.html',
})
export class AddModalPage {
categories:object[] = [];
constructor(public navCtrl: NavController, public navParams: NavParams, public viewCtrl : ViewController, public recordProvider: RecordsProvider) {
}
ngOnInit() {
this.categories = this.recordProvider.getAllExpenseCategories();
console.log(this.categories);
console.log(this.categories.length);
}
public closeModal(){
this.viewCtrl.dismiss();
}
}
Modal HTML
<ion-content padding>
<h1 (click)="getCat()">Hello</h1>
<p>{{categories[0].name}}</p>
<ion-item>
<ion-label>categories</ion-label>
<ion-select>
<ion-option ng-repeat="obj of categories" value="{{obj.name}}">{{obj.name}}</ion-option>
</ion-select>
</ion-item>
</ion-content>
EDIT RecordsProvider
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
#Injectable()
export class RecordsProvider {
getAllExpenseCategories(){
let categories = [];
this.storage.forEach( (value, key, index)=>{
if(key.indexOf("Exp") == 0){
categories.push(value);
}
});
return categories;
}
}
Ionic Storage (localForage) uses async API, so I would make sure you write your methods with it accordingly, I would re-write the getAllExpenseCategories to leverage promise which is returned by storage:
getAllExpenseCategories(){
let categories = [];
this.storage.forEach( (value, key, index)=>{
if(key.indexOf("Exp") == 0){
categories.push(value);
}
}).then(()=>{
return categories;
})
}
In your case it seems like your method was returning empty array to the component, before storage completed its forEach cycle.
Let me know if this helped

Ionic 3 - Unable to load API data on page

I am trying to follow the instructions on https://ionicacademy.com/http-calls-ionic/ and when I convert the call to my service (https://www.oakwoodnb.com/json/events.php), I am able to log the data to the console, but it won't display on screen. Any idea why?
Here is my code:
api.ts
#Injectable()
export class ApiProvider {
constructor(public http: HttpClient) {
console.log('Hello ApiProvider Provider');
}
getVerses() {
return this.http.get('https://www.oakwoodnb.com/json/events.php');
}
daily-verse.ts
export class DailyVersePage {
//Added to retrieve verses
verses: Observable<any>;
constructor(public navCtrl: NavController, public navParams: NavParams, public apiProvider: ApiProvider) {
//Is this a race condition?
this.verses = this.apiProvider.getVerses();
this.verses
.subscribe(data => {
console.log('my data: ', data);
})
}
daily-verse.html
<ion-list>
<p ion-item *ngFor="let verse of (verses | async)?.results">
Title: {{ verse.title }}
</p>
</ion-list>
The API I am calling does fail unless I have the CORS Chrome Extension enabled, but it does log to console when I have it turned on.
I figured out the answer. Posting it for those that may get stuck like I was.
I changed
<p ion-item *ngFor="let verse of (verses | async)?.results">
to
<p ion-item *ngFor="let verse of (verses | async)?.items">
because the data returns in the first element as "items" instead of "results" in the API.

FirebaseList - Unable to access parse data fetched from firebase

I'm getting " Error: InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe' " when running the following code.
html template:
<ion-list>
<ion-item-sliding *ngFor="let item of shoppingItems | async">
<ion-item>
{{ item.$value }}
</ion-item>
<ion-item-options side="right">
<button ion-button color="danger" icon-only (click)="removeItem(item.$key)"><ion-icon name="trash"></ion-icon></button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
page ts:
shoppingItems: AngularFireList<any>;
constructor(public navCtrl: NavController, public firebaseProvider: FirebaseProvider) {
this.shoppingItems = this.firebaseProvider.getShoppingItems();
}
firebase provider
constructor(public afd: AngularFireDatabase) {
console.log('Hello FirebaseProvider Provider');
console.log("Shopping items"+afd.list('/shoppingItems/'))
}
getShoppingItems() {
console.log("Shopping items"+this.afd.list('/shoppingItems/'))
return this.afd.list('/shoppingItems/');
}
addItem(name) {
this.afd.list('/shoppingItems/').push(name);
}
firebase db
shoppingItems
-KxmiUt64GJsPT84LQsI: "qwerty"
-KxmifqyfD41tRPwFh07: "key"
From the web app I was able to add items to firebase db. But I wasn't able to render the data from firebase. I am getting the above mentioned error.
Thanks in advance for your help.
AngularFire2 V5
this.afd.list('/shoppingItems/') does not return an asynchronous observable which is supposed to work with async pipe. It returns a reference to the List in the real-time database.
You need to use valueChanges() to return that.
In your provider return the observable,
getShoppingItems() {
console.log("Shopping items"+this.afd.list('/shoppingItems/'))
return this.afd.list('/shoppingItems/').valueChanges();//here
}
If you are accessing through $key/$value,
Check the upgrade details for angularfire2 v4 to 5. This is because FirebaseList is deprecated and AngularFireList is now returned from version 5.
Calling .valueChanges() returns an Observable without any metadata. If you are already persisting the key as a property then you are fine. However, if you are relying on $key, then you need to use .snapshotChanges()
You need to use snapshotChanges()
getShoppingItems() {
console.log("Shopping items"+this.afd.list('/shoppingItems/'))
return this.afd.list('/shoppingItems/').snapshotChanges();//here
}
To access $key or $value in html,
use item.payload.key and item.payload.val()
Add ionViewDidLoad life cycle in the home.ts file and instead of constructor load your item in ionViewDidLoad , replace your constructor with below code
constructor(public navCtrl: NavController, public firebaseProvider: FirebaseProvider) {
}
ionViewDidLoad() {
this.shoppingItems = this.firebaseProvider.getShoppingItems();
console.log(this.shoppingItems);
}
Import at page.ts
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { AngularFireDatabase} from 'angularfire2/database';
import { Observable } from "rxjs/Observable";
import { ShoppingItem } from '../../model/shoppingitem';
import your provider the right way from your provider
import { FirebaseProvider } from '../../providers/firebase/firebase';
export class Page {
shoppingItems:Observable<ShoppingItem[]>;
constructor(public navCtrl: NavController,
public firebaseProvider: FirebaseProvider) {
this.shoppingItems=this.firebaseProvider
.getShoppingItem()
.snapshotChanges()
.map(
changes=>{
return changes.map(c=>({
key:c.payload.key, ...c.payload.val(),
}));
}
);
}
}
}
firebaseProvider
note the 'shopping-list' is the table at the firebase database
private itemListRef = this.afDatabase.list<ShoppingItem>('shopping-list');
constructor(private afDatabase: AngularFireDatabase) {}
.getShoppingItem(){
return this.itemListRef;
}
}
Your shoppingitem model
export interface ShoppingItem{
key?:string;
}

force view to reload in ionic2 framework

After going through Clear History and Reload Page on Login/Logout Using Ionic Framework
I want to know same question, but for ionic2 using typescript.
On login and logout I need reload the app.ts, because there are classes that run libraries on construct.
it would be basically redirect to home and reload.
Found this answer here, (please note especially the line this.navCtrl.setRoot(this.navCtrl.getActive().component); which is by far the simplest solution that I've come across to reload present page for Ionic 2 & 3 and later versions of Angular (mine is 4), so credit due accordingly:
RELOAD CURRENT PAGE
import { Component } from '#angular/core';
import { NavController, ModalController} from 'ionic-angular';
#Component({
selector: 'page-example',
templateUrl: 'example.html'
})
export class ExamplePage {
public someVar: any;
constructor(public navCtrl: NavController, private modalCtrl: ModalController) {
}
refreshPage() {
this.navCtrl.setRoot(this.navCtrl.getActive().component);
}
}
If you want to RELOAD A DIFFERENT PAGE please use the following (note this.navCtrl.setRoot(HomePage);:
import { Component } from '#angular/core';
import { NavController, ModalController} from 'ionic-angular';
import { HomePage } from'../home/home';
#Component({
selector: 'page-example',
templateUrl: 'example.html'
})
export class ExamplePage {
public someVar: any;
constructor(public navCtrl: NavController, private modalCtrl: ModalController) {
}
directToNewPage() {
this.navCtrl.setRoot(HomePage);
}
}
Ionic 1
I haven't used Ionic 2 but currently i m using Ionic 1.2 and if they are still using ui-router than you can use reload: true in ui-sref
or you can add below code to your logout controller
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
Angular 2
Use
$window.location.reload();
or
location.reload();
You have to implement the CanReuse interface, and override the routerCanReuse to return false. Then, try calling router.renavigate().
Your component should look like this:
class MyComponent implements CanReuse {
// your code here...
routerCanReuse(next: ComponentInstruction, prev: ComponentInstruction) {
return false;
}
}
And then, when you perform login/logout, call:
// navigate to home
router.renavigate()
This is a hack, but it works.
Wrap the logic that follows your template adjustment in a setTimeout and that gives the browser a moment to do the refresh:
/* my code which adjusts the ng 2 html template in some way */
setTimeout(function() {
/* processing which follows the template change */
}, 100);
For ionic 2 it works for me when you force page reload by triggering fireWillEnter on a view controller
viewController.fireWillEnter();
Here is what worked for me to refresh only current page-
I am trying to call refreshMe function when I call onDelete from my view page,
See how my page.ts file looks-
export class MyPage {
lines of code goes here like
public arr1: any;
public arr2: any;
public constructor(private nav: NavController, navParams: NavParams) {
this.nav = nav;
this.arr1 = [];
this.arr2 = [];
// console.log("hey array");
}
onDelete() {
perform this set of tasks...
...
...
refreshMe()
}
refreshMe() {
this.nav.setRoot(MyPage);
}
}
This is just refreshing only current page.
We can also call this function from view if we need as--
<ion-col width-60 offset-30 (click)="refreshMe()">
....
....
</ion-col>
I personally use these three lines to totally refresh a component
let active = this.navCtrl.getActive(); // or getByIndex(int) if you know it
this.navCtrl.remove(active.index);
this.navCtrl.push(active.component);
You can use the ionViewWillLeave() to display your splashscreen while component is reloading and then hide it with ionViewDidEnter() once its loaded.
Hope it helps