I have 2 pages Page1 and Page2. I have used this.nav.pop() in Page2 and it will pop the Page2 and Page1 will enable but i want to refresh the Page1.
Thank you in advance.
you could pass the parent page along with the nav push. that way you could accces the parent page as a navParamter.
in parent page:
goToChildPage() {
this.navCtrl.push(ChildPage, { "parentPage": this });
}
and in the child page before pop you could call functions on parent page
this.navParams.get("parentPage").someFnToUpdateParent();
//or
this.navParams.get("parentPage").someFnToRefreshParent();
Ignore the direct angular implementations suggested here, especially since you are using Ionic 2 and the suggestions are assuming Ionic 1. Don't start mixing too much of direct angular in your ionic app unless there is no ionic implementation for what you need. Import "Events" from ionic/angular2 in both Page1 and Page2, then in Page2 do something like
this.events.publish('reloadPage1');
this.nav.pop();
And in Page1 put
this.events.subscribe('reloadPage1',() => {
this.nav.pop();
this.nav.push(Page1);
});
You may want to implement one of these in your page:
ionViewWillEnter
ionViewDidEnter
Please review the navController and page lifecycle documentation:
http://ionicframework.com/docs/v2/api/components/nav/NavController/
Simple solution that worked for me was calling the get service method again in ionViewDidEnter
ionViewDidEnter() {
this.loadGetService();
}
On PAGE 1:
import { Events } from 'ionic-angular'
constructor(public events:Events){
this.listenEvents();
}
... ...
listenEvents(){
this.events.subscribe('reloadDetails',() => {
//call methods to refresh content
});
}
On PAGE 2:
import { Events } from 'ionic-angular'
constructor(public events:Events, public navCtrl:NavController){
}
function(){
this.events.publish('reloadDetails');
this.navCtrl.pop();
}
You may consider send an event before call this.nav.pop to let page 1 reload itself.
Like Jonathan said, you can import Events from ionic-angular, but you don't need push and pop again, call your methods to reload only the content.
In page2:
this.events.publish('reloadDetails');
this.navCtrl.pop();
In page1:
this.events.subscribe('reloadDetails',() => {
//call methods to refresh content
});
That works for me.
I simply load the details in page 1 in an ionViewWillEnter function (using Ionic 2). This handles both the initial load and any refresh when popping back to page 1.
Documentation is here.
ionViewWillEnter
"Runs when the page is about to enter and become the active page."
I found this technique to reload a page:
this.navCtrl.insert(1, MyPage);
this.navCtrl.pop();
I had the same problem and spend many hours searching and trying the solution.
If I understand, your problem is:
Page 1 have some bindings that you get from an API / Webservice.
Page 2 have some inputs and when pushing the back button (pop) you want to SAVE data + refresh the Page 1 bindings.
The way I solved it has been reading a post on StackOverflow that now I can't find :( !!
The solution is using an Injectable Service.
PAGE 1:
/* IMPORTS */
import { App, Nav, NavParams } from 'ionic-angular';
import { Oportunidades } from '../../services/oportunidades.service';
/* SOME BINDINGS HERE */
{{oportunidades.mesActual.num_testdrive}}
/* CONSTRUCTOR */
constructor(
private oportunidades: Oportunidades, // my injectable service!
public app: App,
public nav: Nav,
public params: NavParams
) {
// Call to the Injectable Service (named oportunidades):
this.oportunidades.getOportunidades();
}
INJECTABLE SERVICE:
/* IMPORTS */
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class Oportunidades {
public url = 'http://your-API.URL';
public data: Observable<Object>;
public mesActual: Object = [];
constructor(private http: Http){
//GET API DATA
this.data = http.get(this.url).map(res => res.json());
}
getOportunidades() {
this.data.subscribe(data => {
this.mesActual = new MesActual(
data["mes_actual_slide"].num_testdrive,
...
//Here I get the API data and set it on my injectable object
);
});
}
}
PAGE 2:
/* SOME IMPORTS */
import { NavController } from 'ionic-angular';
import { UserData } from '../../services/data.service';
import { Oportunidades } from '../../services/oportunidades.service';
import { Http, Headers, URLSearchParams } from '#angular/http';
/* SOME example BINDINGS and INPUTS: */
#Component({
template: `
{{ day[selectedDay].dia }}
Input data:
<ion-input type="number" clearOnEdit="true"
#ventas id="ventas" value={{day[selectedDay].ventas}}
(keyup)="setVal(ventas.value, $event)">
</ion-input>
`,
providers: [
]
})
export class PageInsert {
constructor(
public navCtrl: NavController,
private http: Http,
private userData: UserData,
public oportunidades: Oportunidades // my injectable service!
) {
send(selectedDay){
var url = 'http://your.api.url/senddata';
// I SAVE data TO API / Webservice
this.http.post(url, params, { headers: headers })
.map(res => res.json())
.subscribe(
data => {
console.log(data);
// Here i'll call to the Injectable service so It refresh's the new data on Page1
// in my case, this send function is called when "pop" or "back" button of ionic2 is pressed
// This means: On click on back button -> Save and refresh data of the Injectable that is binded with the Page1
this.oportunidades.getOportunidades();
return true; },
error => {
console.error("Error saving!");
}
);
}
}
I hope it can help you!! Ask for any similar problems :)
I spent a day and a half on a similar issue. The solution is anti-climatic really.
I'm passing a FormGroup from Page-1 to Page-2. I update the FormGroup values in Page-2. When I pop Page-2, Page-1's form is not updated with the new values. It hasn't been watching for changes.
To fix this, I patch the FormGroup with itself after Page-2 has been popped but still in the Page-2 component.
This is more responsive, but requires a direct call to close().
// Page-2 close method
public close(): void {
this.navCtrl.pop();
this.formGroup.patchValue(this.formGroup.value);
}
This is all encompassing, but I do see the refresh on Page-1.
// Page-2 nav controller page event method
ionViewWillUnload() {
this.formGroup.patchValue(this.formGroup.value);
}
In some situations instead of pop() you can use the push() function. When you enter the page with the push() function it is reloaded.
Also you can remove page2 from the navigation.
this.navCtrl.push(TabsPage).then(() => {
const index = this.viewCtrl.index;
this.navCtrl.remove(index);
});
Or if you have more than one page for example page1->page2->pag3:
this.navCtrl.push(TabsPage).then(() => {
const index = this.viewCtrl.index;
this.navCtrl.remove(index, 2); //this will remove page3 and page2
});
ionViewWillEnter() {
this.refresh();
}
ionViewWillEnter will be called just before any time you (re)visualize the page
Please checkout my solution, which I've posted here:
https://forum.ionicframework.com/t/ionviewdidenter-is-not-invoked-on-leaving-a-pushed-page/131144/19?u=unkn0wn0x
Maybe you can adapt it to your needs.
The main point of my intention was to prevent, passing a whole module with this as a navCtrlParam into the push()'ed page, like it was mentioned in some comments before.
Hope it helps!
Cheers
Unkn0wn0x
Related
So I know what is causing the problem I'm just not sure how to fix it..
Basically I have a user service like so..
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Entry } from 'contentful';
#Injectable()
export class UserService {
private userProgressSource = new BehaviorSubject<Object>({});
userProgress = this.userProgressSource.asObservable();
updateUserProgess(progress) {
this.userProgressSource.next(progress);
}
}
then In my program page I run the updateUserProgess() with the users progress
now I have a header that uses that data and both the program page and the header are initialized at the same time so when the page first starts I get heaps of ProgramHeaderComponent.html:28 ERROR TypeError: Cannot read property 'week' of undefined and I know the reason this is happening is because the data hasnt loaded yet because when the data has finished loading I see it where I want it in my header and the errors stop.. How can I prevent the errors on startup?
programHeaderComponent
ngOnInit() {
this._UserService.userProgress.subscribe((progress) => {
this.progress = progress;
console.log(this.progress);
});
}
prorgramHeaderComponent.html
<p>You're up to Week {{progress?.fields.week}}: Step {{progress?.fields.sequenceOrder}}<i class="fa fa-chevron-right"></i>
I'm wondering if there's a way to pass data from a modal to the previous page without using nav controller. If I use that, in fact, I push another page on the stack, and I don't want this, I just want to go back to the existing one. The problem is that if I do:
this.viewCtrl.dismiss();
this.navCtrl.pop();
inside the modal when I want to close it, it gives me an error saying that there is no page to pop.
If I didn't need to pass the data I would simply use the dismiss() method for the modal, but I need to return some data to the previous page and I don't know how to do that. Is it possible? How can I achieve this?
Just like you can see here you can send some data in the dismiss method and get it in the previous page (you don't need to use this.navCtrl.pop();):
// Page A
// ----------
private dataFromModal: any;
presentModal() {
let modal = this.modalCtrl.create(ModalPage);
modal.onWillDismiss((data) => {
// This is going to be executed when the modal is closed, so
// you can get the data here
this.dataFromModal = data;
});
modal.present();
}
// Page B
// ----------
let data = { 'foo': 'bar' };
this.viewCtrl.dismiss(data);
You could store the data in the Storage and then on dismiss fetch the info like so:
import { Storage } from '#ionic/storage';
constructor(private navCtrl: NavController, private navParams: NavParams,
private modalCtrl: ModalController,
private storage: Storage) {}
openModal() {
let modal = this.modalCtrl.create(ModalPage);
modal.present();
modal.onDidDismiss(data => {
this.getInfo();
});
}
getInfo() {
this.storage.get("some_data").then(res => {
if(res != null){
this.data = res;
}
});
}
modal.ts
setInfo() {
this.storage.set('some_data', data);
this.dismiss();
}
I'm working on a feature for my website to provide news feed. To get news feed, I have generated an API key from here : News API. The problem I'm facing is that, I'm not able to display the content in my browser. Let me share my code :
results.component.html
<ul type="none" id="search-options">
<li [class.active_view]="Display('all')" (click)="docClick()">All</li>
<li [class.active_view]="Display('news')" (click)="newsContent()">News</li>
<li [class.active_view]="Display('images')" (click)="imageClick()">Images</li>
<li [class.active_view]="Display('videos')" (click)="videoClick()">Videos</li>
</ul>
In results.component.html, it has 4 tabs. In tabs with name : All, Image, Video - I'm getting data from the server from which the desired results are fetched based on query. But in News tab, I'm trying to integrate it with the API which I have mentioned above. Clicking on that tab, should show news feed (just keeping it simple). But, it does not displays anything when news tab is clicked. (No console errors) However, if I use html repeater, how I'm going to use it here ?
news.component.ts
newsFeed: {};
resultDisplay: string;
constructor(private _newsService: NewsService) {}
Display(S) {
return (this.resultDisplay === S);
}
newsContent() {
this.resultDisplay = 'news';
this._newsService.getNews().subscribe(data => {
this.newsFeed = Object.assign({}, data);
});
}
news.service.ts
import { Injectable } from '#angular/core';
import { Http, Jsonp } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class NewsService {
public generalNews: string = 'https://newsapi.org/v1/articles?source=bbc-news&sortBy=top&apiKey=abc123';
constructor(
private http: Http,
private jsonp: Jsonp
) { }
getNews() {
return this.http.get(this.generalNews).map(response => {
response.json()
});
}
}
It would be great if anybody can point out what mistake I'm doing and provide me the solution to improve it. Thanks in advance. :)
Based on what I got from your question. You should do the following.
Have a repeater on your html first. Something like:
<div *ngFor = "let news of newsList">
<p> {{news}} </p>
</div>
This way you can iterate the news array on your html one news at a time.
Next thing, is getting the response and passing it to the view. I am assuming you get the news on click of <li>, as that is all your view presently contains. So in your component you should have
private newsList: any[] = [] //initializing a blank list of type any.
newsContent() {
this.resultDisplay = 'news'; //not sure why you need this, but let it be for now
this._newsService.getNews().subscribe(data => {
this.newsList = data; //assuming data is the actual news array, if the data is entire response you might have to go for data.data
});
}
This way your newsList variable is populated and will be iterated on the html. May be you might have to make few adjustment but it should help you start.
Let me know if it helps or any further issue faced.
Few more changes would be required based on your response:
First : return data from your service method like:
getNews() {
return this.http.get(this.generalNews).map(response:any => {
return response.json()
});
}
Second, your data contains news in article array. So use that instead:
newsContent() {
this.resultDisplay = 'news'; //not sure why you need this, but let it be for now
this._newsService.getNews().subscribe(data => {
this.newsList = data.article; //assuming data is the actual news array, if the data is entire response you might have to go for data.data
});
}
Next edit your html to bind a particular field of your news. I am binding title you can bind one or more that you like:
<div *ngFor = "let news of newsList">
<p> {{news.title}} </p>
</div>
Try this. should work now.
You have missed return statement return response.json().
news.service.ts
import { Injectable } from '#angular/core';
import { Http, Jsonp } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class NewsService {
public generalNews: string = 'https://newsapi.org/v1/articles?source=bbc-news&sortBy=top&apiKey=abc123';
constructor(
private http: Http,
private jsonp: Jsonp
) { }
getNews() {
return this.http.get(this.generalNews).map(response => {
return response.json();
});
}
}
I have the Tour of Heroes app running, but I want to extend it to make ajax calls.
I have a WebAPI service that serves up the data (CORS enabled) and have proven it w/ a silly little non Angular client using JQuery $.post and $GetJson ... All was going well...
Here is my hero-details.component.ts file
(happy to include any others that may help...)
import {Component , Input, OnInit } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
import { Location } from '#angular/common';
import { HttpModule } from '#angular/http';
import 'rxjs/add/operator/switchMap';
import { Hero } from './hero';
import { HeroService } from './hero.service';
#Component({
selector: 'hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls : ['./hero-detail.component.css']
})
export class HeroDetailComponent { // implements OnInit {
#Input() hero: Hero;
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
submitted = false;
constructor(
private heroService: HeroService,
private route: ActivatedRoute,
private location: Location,
$http //**--LINE OF INTEREST #2**
) { }
ngOnInit(): void {
this.route.params
.switchMap((params: Params) => this.heroService.getHero(+params['id']))
}
save(): void {
this.heroService.update(this.hero)
.then(() => this.goBack());
}
goBack(): void {
this.location.back();
}
onSubmit() { this.submitted = true; }
callService( ) {
var uri = 'http://localhost:61212/api/heros';
//**// LINE OF INTEREST #1**
$http({
method: 'GET',
url: uri
}).then(function () { alert('success'); }, function () { alert('fail');});
};
}
If I try to compile I get
TS2304: Cannot find '$http'
I can comment the $HTTP call (Line of Interest #1 ) and it compiles, it runs and i do enter the function and hit a breakpoint where i declare and assign the variable "uri". So I am reasonably sure I have the problem isolated.
So I believe, based on hours of googling, that I need to DI the $http object into this component
But when I pass $http into the constructor (LINE OF INTEREST #2) I get the following error when I try to compile
TS7006 Parameter '$http' implicitly has an 'any' type
I have googled this so much Larry and Sergy have asked me to knock it off.
What I have found is $http being passed into controllers, maybe Im missing something, but I can not seem to translate those articles into something that works for this.
1) Am I right that injecting the $http object is what needs to be done
2) What is the syntax?
when I was googling , i was just googling angular and most the articles were for angular1. Thats why I was seeing answers that involved controllers, etc.
angular2 is much different. If you are trying to get off the ground, try searching angular2 instead. at least the articles you run across will be relevant.
if you are using visual studio.. here is a nice link to get you started...
https://jonhilton.net/2016/12/01/fast-track-your-angular-2-and-net-core-web-app-development/
I have Ionic 2 app with one view for 3 different data sets. Data are loaded in constructor and based on variable in page params, it's decided which data set to show.
At every successful data call by observable, event handler logs success when data are loaded. But this only works when I click/load view for a first time. If I click for 2nd or any other time, data are not re-loaded (no log). Also, when I just console log anything, it won't show at 2nd+ click.
So I wonder what should I change to load data everytime and how constructor works in this manner.
This is how my code looks like. Jsons are called from namesListProvider.
#Component({
templateUrl: '...',
})
export class ListOfNames {
...
private dataListAll: Array<any> = [];
private dataListFavourites: Array<any> = [];
private dataListDisliked: Array<any> = [];
constructor(private nav: NavController, ...) {
...
this.loadJsons();
console.log('whatever');
}
loadJsons(){
this.namesListProvider.getJsons()
.subscribe(
(data:any) => {
this.dataListFavourites = data[0],
this.dataListDisliked = data[1],
this.dataListAll = data[2]
if (this.actualList === 'mainList') {
this.listOfNames = this.dataListAll;
this.swipeLeftList = this.dataListDisliked;
this.swipeRightList = this.dataListFavourites;
}
else if (...) {
...
}
this.listSearchResults = this.listOfNames;
}, err => console.log('hey, error when loading names list - ' + err),
() => console.info('loading Jsons complete')
)
}
What you're looking for are the Lifecycle events from Ionic2 pages. So instead of using ngOnInit you can use some of the events that Ionic2 exposes:
Page Event Description
---------- -----------
ionViewLoaded Runs when the page has loaded. This event only happens once per page being created and added to the DOM. If a page leaves but is cached, then this event will not fire again on a subsequent viewing. The ionViewLoaded event is good place to put your setup code for the page.
ionViewWillEnter Runs when the page is about to enter and become the active page.
ionViewDidEnter Runs when the page has fully entered and is now the active page. This event will fire, whether it was the first load or a cached page.
ionViewWillLeave Runs when the page is about to leave and no longer be the active page.
ionViewDidLeave Runs when the page has finished leaving and is no longer the active page.
ionViewWillUnload Runs when the page is about to be destroyed and have its elements removed.
ionViewDidUnload Runs after the page has been destroyed and its elements have been removed.
In your case, you can use the ionViewWillEnter page event like this:
ionViewWillEnter {
// This will be executed every time the page is shown ...
this.loadJsons();
// ...
}
EDIT
If you're going to obtain the data to show in that page asynchronously, since you don't know how long would it take until the data is ready, I'd recommend you to use a loading popup so the user can we aware of something happening in the background (instead of showing a blank page for a few seconds until the data is loaded). You can easily add that behaviour to your code like this:
// Import the LoadingController
import { LoadingController, ...} from 'ionic/angular';
#Component({
templateUrl: '...',
})
export class ListOfNames {
...
private dataListAll: Array<any> = [];
private dataListFavourites: Array<any> = [];
private dataListDisliked: Array<any> = [];
// Create a property to be able to create it and dismiss it from different methods of the class
private loading: any;
constructor(private loadingCtrl: LoadingController, private nav: NavController, ...) {
...
this.loadJsons();
console.log('whatever');
}
ionViewWillEnter {
// This will be executed every time the page is shown ...
// Create the loading popup
this.loading = this.loadingCtrl.create({
content: 'Loading...'
});
// Show the popup
this.loading.present();
// Get the data
this.loadJsons();
// ...
}
loadJsons(){
this.namesListProvider.getJsons()
.subscribe(
(data:any) => {
this.dataListFavourites = data[0],
this.dataListDisliked = data[1],
this.dataListAll = data[2]
if (this.actualList === 'mainList') {
this.listOfNames = this.dataListAll;
this.swipeLeftList = this.dataListDisliked;
this.swipeRightList = this.dataListFavourites;
}
else if (...) {
...
}
this.listSearchResults = this.listOfNames;
}, err => console.log('hey, error when loading names list - ' + err),
() => {
// Dismiss the popup because data is ready
this.loading.dismiss();
console.info('loading Jsons complete')}
)
}
The solution is don't do this in the constructor, use ngOnInit() instead. Components are created only once, therefore the constructor will only be called when first created.
Your component class must implement the OnInit interface:
import { Component, OnInit } from '#angular/core';
#Component({
templateUrl: '...',
})
export class ListOfNames implements OnInit {
constructor(...)
ngOnInit() {
this.loadJsons();
}
private loadJsons() {
...
}
}
i'm coming from Angular 2 world, not ionic, but angular 2 has the option to register callbacks on init/destory (ngInit/ngDestory).
try to move initialization to ngInit, save subscription handler, and don't forget to unsubscribe it on destory.
i think your issue related to that you are not unsubscribing.. :\