ion-tab detect a second select - ionic2

I'm having a problem regarding ionic2 with ion-tabs.
When I tap an ion-tab the first time, the constructor for that page (SettingsPage for example) gets called. When I navigate away and tap the same ion-tab again, the constructor doesn't get called (kind of makes sense).
Is there any way for me to call the constructor again? If not the constructor, at least a predefined function.

That's happening because the page used as tab is being created only the first time you're selecting that tab. After that, since the tab already exists, it won't be created again (and thus, the constructor is not going to be called).
If you want to execute some code every time a tab gets selected, use ionViewDidEnter instead:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
#Component({
selector: 'page-a-tab',
templateUrl: 'app/a-tab.page.html'
})
export class ATabPage {
constructor(private navCtrl: NavController) {}
ionViewDidEnter() {
// This code will be executed every time the tab is selected! :)
}
}

Related

Issues with model when navigating while still being retrieved

Working with an older ember application (2.18.1). The following problem is repeated too many times to all fix in the time frame I got available right now.
The component is loading it's own data (setting this.get('model')) and all works fine.
However as the database is now a little slower the user sometimes click on one link, where the template render the component and it start loading it's data .
If the user click another link (to a route that does exactly the same) data from both the previous and the "new" component get loaded.
I can't reset the model when data get loaded, since the fetchRecord method that loads the data get called over and over with paging (as the user scroll down).
I'm sure I'm just not thinking of an obvious solution (did not work on Ember for a few years), any advise?
(ps: some of these components does not use paging, in mean time I'm going to clear out the model before setting it to what the api returns)
I'm afraid ember-data gives no support to abort a request, but you can handle that yourself directly on your component calling the endpoint through Ajax or fetch, and then pushing the payload or aborting requests using the lifecycle hooks. For example, you can trigger the abort() on the willDestroyElement hook.
import $ from 'jquery';
import Component from '#ember/component';
export default Component.extend({
init() {
this._super(...argument);
const xhr = $.get( "ajax/test.html", (data) => {
this.get('store').pushPayload(data);
});
this.set('xhr', xhr);
}
willDestroyElement() {
this._super(...argument);
this.get('xhr').abort()
}
});

Ember init failing to recognize _super

So my app has this component.js:
import Component from '#ember/component';
import layout from './template';
export default class MyComponent extends Component {
layout = layout;
init() {
this._super(...arguments);
}
}
When the component is rendered I am getting this error in the chrome console:
Assertion Failed: You must call `this._super(...arguments);` when overriding `init` on a framework object. Please update <savings-toolkit#component:my-component::ember2445> to call `this._super(...arguments);` from `init`.
The content is not loaded. I wish I could say more, but seriously, what the heck?
Yes, it was initially more much content when I started. It is, however, at this time, literally nothing more than the above.
No one's answering, but I found the answer.
If you are using classes, ie export default class myComponent extends Component as opposed to the old way (export default Component.extend) you shouldn't use this._super. Instead, you use the super keyword:
super.init(...arguments);

How to trigger didReceiveAttrs in Ember component

Using version 2.17. I have an Ember component inside an /edit route with a controller:
// edit.hbs
{{ingredient-table recipe=model ingredients=model.ingredients}}
Inside my component, I am using a didRecieveAttrs hook to loop through ingredients on render, create proxy objects based off of each, and then build an ingredient table using those proxy objects.
// ingredient-table.js
didReceiveAttrs() {
let uniqueIngredients = {};
this.get('ingredients').forEach((ingredient) => {
// do some stuff
})
this.set('recipeIngredients', Object.values(uniqueIngredients));
}
I also have a delete action, which I invoke when a user wishes to delete a row in the ingredient table. My delete action looks like this:
// ingredient-table.js
deleteIngredient(ingredient) {
ingredient.deleteRecord();
ingredient.save().then(() => {
// yay! deleted!
})
}
Everything mentioned above is working fine. The problem is that the deleted ingredient row remains in the table until the page refreshes. It should disappear immediately after the user deletes it, without page refresh. I need to trigger the didReceiveAttrs hook again. If I manually call that hook, all my problems are solved. But I don't think I should be manually calling it.
Based on the docs, it is my understanding that this hook will fire again on page load, and on re-renders (not initiated internally). I'm having some trouble figuring out what this means, I guess. Here's what I've tried:
1) calling ingredients.reload() in the promise handler of my save in ingredient-table.js (I also tried recipe.reload() here).
2) creating a controller function that calls model.ingredients.reload(), and passing that through to my component, then calling it in the promise handler. (I also tried model.reload() here).
Neither worked. Am I even using the right hook?
I suppose recipeIngredients is the items listed in the table. If that is the case; please remove the code within didReceiveAttrs hook and make recipeIngredients a computed property within the component. Let the code talk:
// ingredient-table.js
recipeIngredients: Ember.computed('ingredients.[]', function() {
let uniqueIngredients = {};
this.get('ingredients').forEach((ingredient) => {
// do some stuff
})
return Object.values(uniqueIngredients)
})
My guess is didReceiveAttrs hook is not triggered again; because the array ingredients passed to the component is not changed; so attrs are not changed. By the way; do your best to rely on Ember's computed properties whenever possible; they are in the hearth of Ember design.

Changing query param is not shown in the url

I want to click on a "export" button and transit to a route "home" with "export" query param set as true. I don't want this query param to refresh my route. So here is how my route looks like:
export default Route.extend(ApplicationRouteMixin, {
queryParams: {
export: {
refreshModel: false
}
}
})
In my controller, i'm trying to observe the query param and call a function which does export for me and after that i want to set the query param back to null. here is my controller:
import Ember from 'ember'
const {Controller, inject} = Ember
export default Controller.extend({
// == Dependencies ==========================================================
session: inject.service(),
// == Keyword Properties ====================================================
queryParams: ['export'],
export: null,
queryParamsObserver: function () {
if (this.get('export')) {
this.exportFile()
this.set('export', null)
}
}.observes('export'),
// == Functions =============================================================
exportFile () {
},
// == Actions ===============================================================
actions: {
}
})
But my problem is that when i set the query param to null, it won't change on the url. I'm wondering what i am missing here that is not causing that behavior.
Plus that i wonder if using observing query param is the best solution to trigger some actions.
The problem with your case is related with the fact that your setting of export within controller is too early that query params change is not reflected to url. It is not trivial to learn about Ember.run loops. You should start learning about run loops by reading following.
Take a look at the following twiddle, if you wrap the setting of export within Ember.run.scheduleOnce as in the twiddle then you will see export is cleared in the url. (In fact it is immediately removed; you never see it is becoming true; if you wrap both export function and clearing of export flag within a promise; let's say that will resolved after some seconds; then you will see it will become true then will be removed).
Regarding your question about using observers; I cannot see anything you could do with your current design. The reason is that; you are saying refreshModel is false; hence you do not have any hook methods available in route to trigger export function within controller if you are within the same route already. You need to change your design; what ykaragol suggested could be a good starting point.
First option, using a service and triggering the export via application route or via button component seems more applicable for your situation. You can use it from all of your routes. Users stay at the same route while they are exporting. I would use this option.
Second option is to convert the exportFile to a thennable function. When the exporting is finished, you can clear the queryParams. Here is a working twiddle for you.

How to conditionally mix a mixin depending on device type

I have an app which needs to handle gestures like swipe and tap on mobile devices. I am using ember-gestures addon to achieve that.
Mixing this additional code in a component and adding event handlers is important on mobile devices but completely useless on desktop devices.
How can one mix a mixin conditionally? I would like to detect the device and conditionally mix the mixin if the code is running on a mobile device.
Sample Code:
import Ember from 'ember';
import RecognizerMixin from 'ember-gestures/mixins/recognizers';
export default Ember.Component.extend(RecognizerMixin, {
recognizers: 'swipe',
swipe(e) {
//mobile swipe event
}
});
A quick-n-dirty way is:
import Ember from 'ember';
import mixin from '../mixins/my-mixin';
let component = Ember.Component.extend({
});
if( /*your device control logic would be here.*/ ){
component.reopen(mixin);
component.reopen({
recognizers: 'swipe',
swipe(e) {
//mobile swipe event
}
});
}
export default component;
A better suggestion would be: doing the above logic in an initializer or an instance-initializer. Because this is not about your component logic, this is a cross cutting concern, that should be separated from your component.