I tried passing parameters in push method of ionic2. like this
this.nav.push(SecondPage, {
thing1: data1,
thing2: data2
});
but is there any way to pass parameter in pop().
This is how I achieved it in ionic-3 and find it easier.
Page from where we pop()
this.navCtrl.getPrevious().data.thing1 =data1;
this.navCtrl.getPrevious().data.thing2 =data2;
this.navCtrl.pop();
Page after pop():
public ionViewWillEnter() {
this.thing1 = this.navParams.get('thing1')|| null;
this.thing2 = this.navParams.get('thing2')|| null;
}
I suggest you use Events. All you have to do is to subscribe to an event on the parent page and then publish the event on the child passing the data you want:
// taken from the docs
import { Events } from 'ionic-angular';
constructor(public events: Events) {}
// first page (publish an event when a user is created)
function createUser(user) {
console.log('User created!')
events.publish('user:created', user);
}
// second page (listen for the user created event)
events.subscribe('user:created', (userEventData) => {
// userEventData is an array of parameters, so grab our first and only arg
console.log('Welcome', userEventData[0]);
});
Currently, I believe that there is no way of accomplishing this.
There is a Github issue for it though, that has got some great discussion on it by the Ionic core team. It sounds like they have added it to the Ionic 2 roadmap, too! The Github issue also has some proposed work-arounds, such as adding the ParentPage to the NavParams going to the ChildPage, but it is all quite a bit hacky.
UPDATE: IT WAS SUPPOSED TO WORK, BUT IT DOES NOT
Seems like there is |See Doc Reference|
pop(opts) takes one parameter of type object
so
to go one step back
this.nav.pop({
thing1: data1,
thing2: data2
});
and to go to a specific view in the history stack
this.nav.popTo(SecondPage, {
thing1: data1,
thing2: data2
});
and to go to root of the stack
this.nav.popToRoot({
thing1: data1,
thing2: data2
});
To retrieve the params (I guess this should work. untested!)
export class SecondPage{
constructor(params: NavParams){
this.params = params;
console.log(this.params.get('thing1'));
}
}
Use popTo() instead of pop()
popTo() has params? parameter where you can pass in your paramaters like so:
this.nav.popTo(SecondPage, {
thing1: data1,
thing2: data2
});
pass in a callback when transitioning by aaronksaunders in this forum
https://forum.ionicframework.com/t/solved-ionic2-navcontroller-pop-with-params/58104/4
Going to try it out.
For sent data with pop you can use getPrevious() method.
When leaving from current page get previous page and send data
ionViewWillLeave() {
this.navCtrl.getPrevious().data.formData = {credentials: {...}};
}
In next page get data from navParams
ionViewWillEnter() {
if (this.navParams.get('formData')) {
// do something
}
}
If you are using ionic-angular application, you can use ionic-angular Events
page1.ts
import { Events,NavController } from 'ionic-angular';
export class page1 {
constructor(private events: Events,
private nvCtrl: NavController
) {}
goToPage2() {
this.navCtrl.pop();
this.event.publish('your-event');
}
}
page2.ts
import { Events} from 'ionic-angular';
export class page1 {
constructor(private events: Events,
private nvCtrl: NavController
) {}
ionViewDidLoad() {
this.events.subscribe('your-event');
}
}
Related
Hi i'm building a chat app with angular for a school project i'm using firebase for my backend and i have an issue with my ngfor.
For exemple if i reload the page i will see nothing unless i hover my routerlink on my navbar. However sometime it will work after some time on the page without any action
When i recieve message i need to be on the page to see them ...
When i reload my page in first time my array is empty this may be what makes the ngfor bug
array on reload.
I'm using ngOnInit() to subscribe :
messages: Message[];
messageSubscription: Subscription;
constructor(private messageService: MessageService, private router: Router) {
}
ngOnInit(): void {
this.messageSubscription = this.messageService.messageSubject.subscribe(
(messages: Message[]) => {
console.log(messages)
this.messages = messages;
}
);
this.messageService.getMessage();
this.messageService.emitMessage();
}
ngOnDestroy(): void {
this.messageSubscription.unsubscribe();
}
This is my html template :
<div *ngFor="let message of messages" class="message-box">
<img [src]="message.photoURL" class="profile-picture">
<div class="content-box">
<div class="information">
<p class="username">{{message.displayName}}</p>
<p class="date">{{message.createdAt | date: 'short'}}</p>
</div>
<p class="text">{{message.text}}</p>
</div>
</div>
Here you can find my service with my getMessage() function and emitMessage():
messages:Message[] = [];
messageSubject = new Subject<Message[]>();
constructor() { }
emitMessage(){
this.messageSubject.next(this.messages);
}
saveMessage(newMessage: Message){
firebase.database().ref('/message').push(newMessage);
}
getMessage(){
firebase.database().ref('/message')
.on('value', (data) => {
this.messages = data.val() ? Object.values(data.val()): [];
this.emitMessage();
});
}
And this is the repo of my project: https://github.com/Zuxaw/AngularChatApp
If anyone has a solution I'm interested
Problem is, your firebase library is not Angular specific.
This means you some times need to make sure its code, mostly its event callbacks, run within an Angular zone (google to read about it) to make sure a change detection 'tick' is invoked when data changes.
message.service.ts
import { Injectable, NgZone } from '#angular/core';
// ...
constructor(private zone: NgZone) { }
// ..
getMessage(){
firebase.database().ref('/message')
.on('value', (data) => {
this.zone.run(() => {
this.messages = data.val() ? Object.values(data.val()): [];
this.emitMessage();
});
});
}
I think you might need to use the child_added event instead of value in your getMessage method.
Check if you're receiving data on time in your getMessage method, if not it's most probably, because of the event.
But one thing that I don't understand is why you're calling emitMessage inside getMessage and also calling it inside your component after getMessage, try to evade that.
Currently, I have a modal material dialog window that asks the user to input a number and then hit search. On search, it fetches data from api call and gets back a response object. I want to use the response object to populate a new page (edit form).
My question is, how can I past the data, particularly the number the user entered on the material dialog component to another component, so that it can fetch the api call results or how can I pass my response object to my edit from from dialog?
E.g.
Here's my search function:
search(searchNumber) {
if (this.selectedOption === 'Bill Number') {
this._transactionService.findExistingTransactionByBillNumber('U001', searchNumber)
.subscribe(data => this.transactionResponse = data);
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal']);
} else {
this._transactionService.findExistingTransactionByTransactionNumber('U001', searchNumber)
.subscribe(data => this.transactionResponse = data);
console.log(JSON.stringify(this.transactionResponse));
this.router.navigate(['/edit-transaction-portal']);
}
}
I want to be able to either 1) pass the response object I get here or pass the searchNumber the user entered, so that I can do a lookup within my edit form component. I need to pass in either one from this component to my new component that I navigate to.
EDIT: Accepted solution shows how to add query params to this.router.navigate() and how to retrieve it by subscribing to activateRoute, a different approach than the one identified in the other SO post.
You can pass the number (bill/transaction)
this.router.navigate(['/edit-transaction-portal'], { queryParams: { bill: 'U001' } });
this.router.navigate(['/edit-transaction-portal'], { queryParams: { transaction: 'U001' } });
then in your component(edit-transaction-portal) hit the api to get the data. In component you should include ActivatedRoute in constructor. It will be something like:
isBill: boolean;
isTransaction: boolean;
number: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.sub = this.route
.queryParams
.subscribe(params => {
this.isBill = params['bill'] != undefined;
this.isTransaction = params['transaction'] != undefined;
this.number = this.isBill ? params['bill'] : params['transaction'];
// Call API here
});
}
My question is, how can I past the data, particularly the number the
user entered on the material dialog component to another component
You can pass it throw material dialog component. Inject dialogRef to you component which opened in the dialog:
constructor(
public dialogRef: MatDialogRef<SomeComponent>,
#Inject(MAT_DIALOG_DATA) public data: any,
) { }
After the submitting data, you can pass any data to component which opened this dialog, by closing the dialog:
onSubmit() {
this.service.postProduct(this.contract, this.product)
.subscribe(resp => {
this.dialogRef.close(resp);
});
}
And in your Parent component, who opened this dialog can get this passed data by subscribing to afterClosed() observable:
Parent.component.ts:
openDialog(id) {
const dialogRef = this.dialog.open(SomeComponent, {
data: { id: anyData}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
// do something...
}
});
}
Would I pass the data object in dialog.open()? How would I retrieve it
from there?
Look at openDialog() above. It has data property, that you can send to dialog components. And in the opened component inject MAT_DIALOG_DATA as this:
#Inject(MAT_DIALOG_DATA) public data: any,
to access passed data object as shown code above
Official docs[sharing-data-with-the-dialog-component]
if you want to pass data which the help of routing you have to define route which takes value as part of rout like as below
const appRoutes: Routes = [
{ path: 'hero/:id', component: HeroDetailComponent },];
it will from code side
gotoHeroes(hero: Hero) {
let heroId = hero ? hero.id : null;
// Pass along the hero id if available
// so that the HeroList component can select that hero.
// Include a junk 'foo' property for fun.
this.router.navigate(['/heroes', { id: heroId, foo: 'foo' }]);
}
Read : https://angular.io/guide/router#router-imports
If you want to pass data between two component then there is #Input and #Output property concept in angular which allows you to pass data between components.
#Input() - this type of property allows you to pass data from parent to child component.
Output() - this type of property allows you to pass data from child to parent component.
Other way to do it is make use of Service as use the same instance of service between component.
Read : 3 ways to communicate between Angular components
I wonder if there is a way to execute something after i navigate to a different "view" using angular router.
this.router.navigate(["/search", "1", ""]);
// Everything after navigate does not not get executed.
this.sideFiltersService.discoverFilter(category);
this.router.navigate returns a promise so you can simply use:
this.router.navigate(["/search", "1", ""]).then(()=>{
// do whatever you need after navigation succeeds
});
// In javascript
this.router.navigate(["/search", "1", ""])
.then(succeeded => {
if(succeeded)
{
// Do your stuff
}
else
{
// Do some other stuff
}
})
.catch(error => {
// Handle the error
});
// In typescript you can use the javascript example as well.
// But you can also do:
try
{
let succeeded = await this.router.navigate(["/search", "1", ""]);
if(succeeded)
{
// Do your stuff
}
else
{
// Do some other stuff
}
}
catch(error)
{
// Handle the error
}
Not entirely sure of the context but an option would be to subscribe to a change in the URL using ActivatedRoute
https://angular.io/docs/ts/latest/guide/router.html#!#activated-route
Here's an example:
...
import { ActivatedRoute } from '#angular/router';
...
private _routerSubscription: any;
// Some class or service
constructor(private _route: ActivatedRoute){
this._routerSubscription = this._route.url.subscribe(url => {
// Your action/function will go here
});
}
There are many other observables you can subscribe to in ActivatedRoute which are listed in that link if url isn't quite what you need.
The subscription can be done in the constructor() or in an ngOnInit() depending on what suits you best, just remember to clean up after yourself and unsubscribe in an ngOnDestroy() :)
this._routerSubscription.unsubscribe();
If you are navigated from ComponentA to ComponentB then after navigating you can do any actions in ngOnInit() function of ComponentB, depending upon the parameters passed in the route.
You also have to ensure that there are no ongoing subscriptions... I faced the same problem and in my case there was a subscription which changed route. So the route has been changed twice. But practically you can use promises, thats right
Similar question to Angular2 Get router params outside of router-outlet but targeting the release version of Angular 2 (so version 3.0.0 of the router). I have an app with a list of contacts and a router outlet to either display or edit the selected contact. I want to make sure the proper contact is selected at any point (including on page load), so I would like to be able to read the "id" param from the route whenever the route is changed.
I can get my hands on routing events by subscribing to the router's events property, but the Event object just gives me access to the raw url, not a parsed version of it. I can parse that using the router's parseUrl method, but the format of this isn't particularly helpful and would be rather brittle, so I'd rather not use it. I've also looked all though the router's routerState property in the routing events, but params is always an empty object in the snapshot.
Is there an actual straight forward way to do this that I've just missed? Would I have to wrap the contact list in a router-outlet that never changes to get this to work, or something like that?
If any body was looking for the latest solution of this issue (angular 8) I stumbled upon this article which worked very well for me.
https://medium.com/#eng.ohadb/how-to-get-route-path-parameters-in-an-angular-service-1965afe1470e
Obviously you can do the same implementation straight in a component outside the router outlet and it should still work.
export class MyParamsAwareService {
constructor(private router: Router) {
this.router.events
.pipe(
filter(e => (e instanceof ActivationEnd) && (Object.keys(e.snapshot.params).length > 0)),
map(e => e instanceof ActivationEnd ? e.snapshot.params : {})
)
.subscribe(params => {
console.log(params);
// Do whatever you want here!!!!
});
}
In the hope to spare the same struggle I went through.
I've been struggling with this issue for the whole day, but I think I finally figured out a way on how to do this by listening to one of the router event in particular. Be prepared, it's a little bit tricky (ugly ?), but as of today it's working, at least with the latest version of Angular (4.x) and Angular Router (4.x). This piece of code might not be working in the future if they change something.
Basically, I found a way to get the path of the route, and then to rebuild a custom parameters map by myself.
So here it is:
import { Component, OnInit } from '#angular/core';
import { Router, RoutesRecognized } from '#angular/router';
#Component({
selector: 'outside-router-outlet',
templateUrl: './outside-router-outlet.component.html',
styleUrls: ['./outside-router-outlet.component.css']
})
export class OutSideRouterOutletComponent implements OnInit {
path: string;
routeParams: any = {};
constructor(private router: Router) { }
ngOnInit() {
this.router.events.subscribe(routerEvent => {
if (routerEvent instanceof RoutesRecognized) {
this.path = routerEvent.state.root['_routerState']['_root'].children[0].value['_routeConfig'].path;
this.buildRouteParams(routerEvent);
}
});
}
buildRouteParams(routesRecognized: RoutesRecognized) {
let paramsKey = {};
let splittedPath = this.path.split('/');
splittedPath.forEach((value: string, idx: number, arr: Array<string>) => {
// Checking if the chunk is starting with ':', if yes, we suppose it's a parameter
if (value.indexOf(':') === 0) {
// Attributing each parameters at the index where they were found in the path
paramsKey[idx] = value;
}
});
this.routeParams = {};
let splittedUrl = routesRecognized.url.split('/');
/**
* Removing empty chunks from the url,
* because we're splitting the string with '/', and the url starts with a '/')
*/
splittedUrl = splittedUrl.filter(n => n !== "");
for (let idx in paramsKey) {
this.routeParams[paramsKey[idx]] = splittedUrl[idx];
}
// So here you now have an object with your parameters and their values
console.log(this.routeParams);
}
}
I'm having some difficulties with Ionic 2 and setting up global variables. The structure of my app is as follows:
Main app
|
|--- Page1 (Info)
|--- Page2 (Map)
|--- Page3 (List)
|
|--- ItemTabsPage
|
|---tab1
|---tab2
|---tab3
My intention is to show a list in Page3, and once one item is selected, to show additional information in tabs.
I send the information from Page 3 to the page with the tabs using:
itemTapped(event, item) {
this.nav.push(ItemTabsPage, {
item: item
});
}
The problem is that I can't do the same to send the info to the child tabs. I would like to show different information depending on which item is selected. I have tried defining an injectable globalVars.js to store the value in a variable:
import {Injectable} from 'angular2/core';
#Injectable()
export class GlobalVars {
constructor(myGlobalVar) {
this.myGlobalVar = "";
}
setMyGlobalVar(value) {
this.myGlobalVar = value;
}
getMyGlobalVar() {
return this.myGlobalVar;
}
}
and then updating the code of itemTapped in the list as follows:
itemTapped(event, item) {
this.nav.push(ItemTabsPage, {
item: item
});
this.globalVars.setMyGlobalVar(item);
}
However, I always get the same error:
Uncaught EXCEPTION: Error during evaluation of "click"
ORIGINAL EXCEPTION: TypeError: Cannot read property 'setMyGlobalVar' of undefined
The code for page3 is:
import {Page, NavController, NavParams} from 'ionic-angular';
import {ItemService} from '../services/ItemService';
import {ItemTabsPage} from '../item/item-tabs/item-tabs';
import {GlobalVars, setMyGlobalVar} from '../../providers/globalVars';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map';
#Page({
templateUrl: 'build/pages/item-list/item-list.html',
providers: [ItemService]
})
export class ItemListPage {
static get parameters() {
return [[NavController], [NavParams], [Http]];
}
constructor(nav, navParams, http, globalVars) {
this.nav = nav;
// If we navigated to this page, we will have an item available as a nav param
this.selectedItem = navParams.get('item');
this.http = http;
//this.items = null;
this.globalVars = globalVars;
this.http.get('https://website-serving-the-info.com/items.json').map(res => res.json()).subscribe(data => {
this.items = data.items;
},
err => {
console.log("Oops!");
});
}
itemTapped(event, item) {
this.nav.push(ItemTabsPage, {
item: item
});
this.globalVars.setMyGlobalVar(item);
}
}
Anyone have any suggestion? My Ionic installation is:
Cordova CLI: 6.1.1
Gulp version: CLI version 3.9.1
Gulp local: Local version 3.9.1
Ionic Framework Version: 2.0.0-beta.4
Ionic CLI Version: 2.0.0-beta.25
Ionic App Lib Version: 2.0.0-beta.15
OS: Distributor ID: LinuxMint Description: Linux Mint 17.3 Rosa
Node Version: v5.11.0
The easiest way I use is to create a file app/global.ts
export var global = {
myvar : 'myvar 01',
myfunction : function(msg) {
alert(msg);
}
};
Then import and use freely in other classes:
import {global} from "../../global";
constructor() {
global.myfunction('test');
}
and if you want to use this global to component HTML page as below
export class HomePage {
Global: any = Global;
now it is available in HTML as below
<div [style.display]="Global.splash ? 'flex': 'none'" >
You're on the right track. And some of the other answers will work, but the Ionic team is recommending you not use globals via a globals file. Instead, they recommend the use of Providers (as you're attempting to do).
You're provider is missing the actual variable declaration.
#Injectable()
export class GlobalVars {
myGlobalVar: string = '' // this is the line you're missing
constructor(myGlobalVar) {
this.myGlobalVar = "";
}
}
You should also note that you are not exporting the function setMyGlobalVar(). You are exporting the class GlobalVars which contains the function setMyGlobalVar().
I believe if you make those changes it should work.
edit
I'd also be careful of this line this.globalVars = globalVars; in your Page3. This will cause a rewrite of your globalVars each time Page3 is created.
I have exactly the same scenario, and would like to share my approach.
my understanding is that, in ionic2, the injection is implemented as instance. which means each time you enter a page, a new instance of the injection is created.
so direct access to a static value does not fit here; you have to somehow bridge the gap.
my approach goes as this:
you still defined a static value in your service provider, yet you define instance "getter", and "setter" for that value.
in your page implementation, you inject the service as a parameter of the constructor.
in the constructor, you have to "new" an instance of the service; and call the "getter", and "setter". see my code snippets below:
export class TransSender {
static _count:number = 0;
static _pushed:number = 0;
...
public static setter(count:number, pushed:number,...) {
TransSender._count = count;
TransSender._pushed = pushed;
}
public get count(){
return TransSender._count;
}
public get pushed(){
return TransSender._pushed;
}
...
}
I actually provide a static collective setter for the service to get value from backend in a static way.
my page implementation runs likes this
import {TransSender} ...;
#Component({
templateUrl: 'build/pages/basics/basics.html',
providers: [TransSender]
})
export class Page {
...
constructor(tSender: TransSender,...) {
...
tSender = new TransSender();
TransSender.setter(1,2,3,4,5,6,7,8);
console.log(tSender.count);
}
}
in your display (html), your will refer to tSender rather than TransSender
this might look a bit stupid. yet I can not find any other solution.
with the release of ionic2 Beta9, bootstrap was re-introduced into the frame. so I am exploring new possibilities
cheers
In your class ItemListPage, try this static parameters method before your constructor:
static get parameters() {
return [[NavController], [NavParams], [Http], [GlobalVars]];
}
I am thinking that you are setting your globalVars variable in the constructor to 'undefined' and therefore you cannot call a function on something that is undefined.
You seem to inject the GlobalVars provider incorrectly in ItemLisyPage.