Passing params without pushing page

Is there a way to pass parameters into another page for later without pushing the view to that page?

Can you describe what you’re really trying to achieve?

I was trying to load data and display it on the page while it was loading but it would give me errors because it had not loaded the data yet. So I was going to try to load the data in the home page first then pass it to the other page. I think I might have figured it out a workaround to the errors with an *ngIf statement in ion-content.

my main problem is setting a default if no data is present but, if there is saved data, to ignore the default and use the saved data instead.

Why not making use of Ionic adaptations of Angular listening states? (suggestion)

(Aka ionViewDidLoad, ionViewWillEnter, ionViewDidEnter, ionViewWillLeave ,ionViewDidLeave, ionViewWillUnload)

I’m not sure of the context, but if you mean with a listing, you can skip that part and use *ngif in the template view (eg .html file).

For example:

    <ion-item *ngFor="let pinguin of pinguins">
        <span *ngIf="pinguin">{{pinguin.name}}</span>
    </ion-item>

The Ngif statement also works with objects.

1 Like

This is my suggestion: to set a reasonable default for all properties that are referenced by the template in the constructor. If you are looping across arrays with ngFor, initialize them to [], for example.

I don’t know how you are retrieving your “saved data”, but typically that comes from a provider that gives you a future, like a Promise or Observable, so it would look like:

<div *ngFor="let item of items"></div>
class ItemProvider {
  constructor(private _http: Http) {}
  allItems(): Observable<Item[]> {
    return this._http.get('/api/items').map(rsp => rsp.json());
  }
}

class ItemListPage {
  items = [] as Item[];

  constructor(private _provider: ItemProvider) {
    this.refresh();
  }

  refresh(): void {
    this._provider.allItems().subscribe(items => this.items = items);
  }
}

I would emphatically not try to load data for page B from inside page A: that is a recipe for overly tight coupling disaster. Each page needs to be responsible for itself as much as is absolutely possible. You will end up with easier debugging and testing that way, because the sphere of potential problem code is much more tightly constrained.

EDIT: P.S. I would also avoid using *ngIf here as is suggested above. It can cause unusual interactions with Ionic’s components, and isn’t necessary in this case.

1 Like

I agree 100%, initializing variables with a default array value, often saves a lot of time and hassle debugging.

And nice technique rapropos, I’d probably test it out in my pages, if using Http to fetch lists or objects.

PS: My example was not for a service but a single view. Oversubscribing to promises or observables,
can also lead to dumb crashes.

Can you elaborate a bit here?

When I try to initialize in the constructor I get errors

settingmodels: SettingModel[] = [];

constructor() {
this.settingmodels[0].listOrder = true; //Gives error that this.settingmodels[0] is undefined
this.settingmodels.listOrder = true; //Gives red underline error saying that listOrder does not exist.
}

It’s a settings page so a for statement does not work for me unless its over the entire page but that gives me a blank page.
Are you saying not to use an *ngIf statement in ion-content?
<ion-content padding *ngIf=“this.settingmodels[0]”>

Using the ionViewDidLoad etc. does not work and gives me errors saying that things are undefined still.

And I take an approach that is a bit of both of the previous approaches. I try to keep my constructors light, because Ionic builds pages before entering them, so I want as little computation in the constructor as possible. Just set your variable to new Array<typeName>() in the constructor or the declaration. (Or to [] as suggested – uglifyjs will make this change automatically anyway when you compile.) Then inside ionViewDidLoad() or ionViewWillEnter() I put the more “hardcore” variable definitions, so the expensive variable initializaton doesn’t occur until I need it to.

This makes the page load with blanks (I also use image placeholders, sort of like the Twitter egg), which are then replaced by the real data as it becomes available.

Edit: to another point in the thread: *ngIf has worked correctly for me 100% of the time inside <ion-item>, and has repeatedly done bizarre things inside <ion-card>. I’ve never tried to find out why. But there’s definitely a gap here between documentation and reality.

1 Like

It’s really hard to answer questions like this without seeing the actual code and what is desired. Both of those errors seem pretty self-explanatory. If you initialize something to an empty array, its 0 element isn’t going to be an object, so you can’t assign to its properties. An array also isn’t going to have a listOrder property.

I’m going to assume you don’t really want an array:

interface SettingModel {
  listOrder: boolean;
}

class Page {
  settings = {listOrder: true} as SettingModel;
}

[quote=“AaronSterling, post:10, topic:86054”]
I try to keep my constructors light, because Ionic builds pages before entering them, so I want as little computation in the constructor as possible. […] Then inside ionViewDidLoad() or ionViewWillEnter() I put the more “hardcore” variable definitions, so the expensive variable initializaton doesn’t occur until I need it to.[/quote]

I used to be a believer in this as well, until I started using reactive forms. I had all sorts of trouble using FormBuilder in Ionic lifecycle events, and eventually said “$##!$* it” and went back to doing most of my setup in constructors.

I haven’t run into that yet, though I often feel as though I am slowly converging to points you arrived at long ago. But ok: my main environment is onPush change detection, 100% use of FormBuilder, no use of ngModel. I define a FormGroup variable in the constructor with every field set to empty string etc., then redefine the FormGroup when about to enter the page. I also define getters in the controller of form get variableName() { return this.formName['variableName'] }; which makes the template much easier to read, no idea if it helps avoid issues of the sort you raise. That approach has worked perfectly so far, but maybe I am sidestepping issues because I don’t use full two-way binding.

This is mainly if you directly listen to some data source as observable / promise. For example, if you work with a real time data source (eg Firebase), it might forget to destroy them on a disconnect event.

I get that.
Im trying to modify code that Im using to save and create a list so it may not be the best way to save and present settings.
Im using a setting model

import { Observable } from 'rxjs/Observable';

export class SettingModel {

  settingmodel: any;
  settingObserver: any;

  constructor(public listOrder: boolean){

this.settingmodel = Observable.create(observer => {
    this.settingObserver = observer;
});

 }

and a data storage injectible

getSettingData(): Promise<any> {
	return this.storage.get('settingmodels');  
}

saveSettings(data): void {

	let saveSettings = [];
	
	data.forEach((settingmodel) => {
		saveSettings.push({
			listOrder: settingmodel.listOrder
		});
	});

	let settingData = JSON.stringify(saveSettings);
	this.storage.set('settingmodels', settingData);

and then loading it into the settings page

ionViewWillLoad(){

this.platform.ready().then(() => {

  this.dataStorage.getSettingData().then((settingmodels) => {

    let savedSettingmodels: any = false;

    if (typeof (settingmodels) != "undefined") {
      savedSettingmodels = JSON.parse(settingmodels);
     
    }

    if (savedSettingmodels) {

      savedSettingmodels.forEach((savedSettingmodel) => {

        let loadSettingmodel = new SettingModel(savedSettingmodel.listOrder);
        
       
       if(this.settingmodels.length <= 0){
        this.settingmodels.push(loadSettingmodel);
       }
        console.log("Loading older settingmodels");
        
        loadSettingmodel.settingmodel.subscribe(update => {
          this.save();

        });
        this.save();
      });
    }

  });
}
save(): void{
  this.dataStorage.saveSettings(this.settingmodels);
}

and then within ionViewWillLoad() Ive been trying to set the default like this but it overwrites any changes made because of my if statement misfiring with the way I’m loading data

if(this.settingmodels.length <= 0) {

                let data = {
                    listOrder: true
                  }
                    let loadSettingmodels = new SettingModel(
                        data.listOrder,
                        );
                        this.settingmodels.push(loadSettingmodels);
                        
                          loadSettingmodels.settingmodel.subscribe(update => {
                            this.save();
                          });
                          this.save();
            } else if (this.settingmodels.length >= 0){
              
            }

in the HTML im trying to display it like

<ion-content padding *ngIf="this.settingmodels[0]">
<div> 
<ion-list>
<ion-item>
  <ion-label> Display List Newest to Oldest </ion-label>
  <ion-checkbox item-right [checked]="this.settingmodels[0].listOrder" (click)="orderList(settingmodel)"> </ion-checkbox>
</ion-item>
</ion-list>
</div>
<ion-content

Hello Aaron,

First I’d like to thank you for your insights on many things which helped me build my application to the point it is now :slight_smile:

As for ion-card and ngif* issue, I really thought it was my code to blame, again you shed some light on obscure things that lacks of bit of documentation (or were sometimes closed a bit too fast on Github).

1 Like

And you’re right, it’s an approach I had no success with with Ionic 2, but I suggested it as maybe people would have some suggestions and better implementations than me. It seems not.

1 Like

That looks like a good way to do it, if you need complex data handling. It could be coupled with a service / provider I guess.
But what I don’t get is why this is not prefiltered with simplier functions? Like map?

Regards,

1 Like

OK, you don’t have to adopt my rules, but here are some:

  • Data is dumb. Trying to make it smart causes more work than it’s worth.

So: no Observables in SettingModel, and in fact I would not even make it a class:

export interface SettingModel {
  listOrder: boolean;
  // whatever else goes in here
}
  • Use any as sparingly as absolutely possible. It helps tsc help you, and it really helps maintainers reading your code understand what you intend (who may very well be you in a few months).

So:

export class SettingsProvider {
  getSettings(): Promise<SettingModel[]> {
    return this.storage.get('settingmodels');
  }

  setSettings(settings: SettingModel[]): void {
    this.storage.set('settingmodels', settings);
  }
}
export class Page {
  settingModels: SettingModel[] = [{listOrder: true}];

  constructor(private _provider: SettingsProvider) {
    _provider.getSettings().then((models) => {
      if (models) {
        this.settingModels = models;
      }
    });
  }
}
1 Like

Thanks for the advice.

I ended up using a for loop in the .ts file for the page when loading the data.

The way I was loading data loads like a -for loop-, so the -if statement- I was using for the default would fire every time, since initially the array length is always 0, when the data was first loading. By putting that process in a -for loop- I can separate it from the -if statement- and evaluate the final length of the array rather than every time it pulls data.

When I tried your solution I was getting errors at

export class Page {
settingModels: SettingModel[] = [{listOrder: true}]; //the listOrder: true is red underlined

also i was not sure if this went in its own page? or where it was injected. Could have caused the problem above maybe?

export interface SettingModel {
listOrder: boolean;
// whatever else goes in here
}