Related
I am trying to upgrade from an ancient version of ember 1.7 to the still ancient 1.13 and I am getting .js errors. I am not well versed with ember, to begin with, so all the help I can get will be appreciated.
I changed all instances of the code where it was using Ember.View.extend to Ember.Controller.extend
and since then, I am getting an error
Cannot read property 'noSignedUpAppts' of undefined.
Appt.Ember.js
function initEmberApp() {
Ember.Handlebars.registerBoundHelper('stringFormat', function (g11nString, str) {
return new Handlebars.SafeString(stringUtil.format(g11nString, str));
});
ApptApp = Ember.Application.create({
rootElement: "#appointmentWrapper"
});
ApptApp.Router.map(function () {
this.route("main", { path: '/' });
this.route("mainParam", { path: "/:val" });
this.route('mainId', { path: '/mainId/:appt_id' });
this.route('mainDirect', { path: '/mainDirect/:appt_id' });
this.route("schedule");
this.resource('userSchedule', { path: '/schedule/:user_id' });
this.route('scheduleId', { path: '/scheduleId/:appt_id' });
this.route('userScheduleId', { path: '/userScheduleId/:ids' });
this.route('scheduleSpecific');
this.route('scheduleSpecificId',{path: '/scheduleSpecificId/:appt_id'});
this.route('scheduleDate', { path: '/scheduleDate/:date' });
this.route('scheduleResource', { path: '/scheduleResource/:resource_id' });
this.route("settings");
this.route("campus");
this.route("noAccess");
});
ApptApp.history = {name:'',view:'',param:null, isSchedule:false}
initViews();
}
function initViews() {
initMain(ApptApp);
initSchedule(ApptApp);
initSettings(ApptApp);
initCampus(ApptApp);
initNoAccess(ApptApp);
}
ApptMain.js
function initMain(ApptApp) {
ApptApp.MainController = Ember.Controller.extend({
g11n: apptPortlet.g11n,
ableToEdit: apptPortlet.ableToEdit,
canManageSettings: apptPortlet.canManageSettings,
canAdmin: apptPortlet.canAdmin,
defaultDate: '',
showMySchedule: false,
apptId: '',
isDirect: false
});
ApptApp.MainParamRoute = Ember.Route.extend({
model: function (param) {
var date = moment(param.val.replace(/-/g, '/'));
return { defaultDate: (date.isValid() ? date.format('MM/DD/YYYY') : ''), showMySchedule: (param.val == 'myS') };
},
setupController: function (controller, model) {
this.controllerFor('main').set('defaultDate', model.defaultDate).set('showMySchedule', model.showMySchedule).set('apptId', '').set('isDirect',false);
},
renderTemplate: function () {
this.render('main');
}
});
ApptApp.MainIdRoute = Ember.Route.extend({
model: function (param) {
var items = param.appt_id.split('|');
var date = moment(items[1].replace(/-/g, '/'));
return { apptId: items[0], defaultDate: (date.isValid() ? date.format('MM/DD/YYYY') : '') };
},
setupController: function (controller, model) {
this.controllerFor('main').set('defaultDate', model.defaultDate).set('showMySchedule', false).set('apptId', model.apptId.toLowerCase()).set('isDirect', false);
},
renderTemplate: function () {
this.render('main');
}
});
ApptApp.MainDirectRoute = Ember.Route.extend({
model: function (param) {
var items = param.appt_id.split('|');
var date = moment(items[1].replace(/-/g, '/'));
return { apptId: items[0], defaultDate: (date.isValid() ? date.format('MM/DD/YYYY') : '') };
},
setupController: function (controller, model) {
this.controllerFor('main').set('defaultDate', model.defaultDate).set('showMySchedule', apptPortlet.apptSetting != 0).set('apptId', model.apptId.toLowerCase()).set('isDirect', true);
},
renderTemplate: function () {
this.render('main');
}
});
ApptApp.MainView = Ember.Component.extend({
didInsertElement: function() {
var controller = this.get('controller');
if (this.processRedirect(controller))
return;
this.renderView(controller);
},
renderView: function (controller, isRerender) {
if (isRerender) {
apptFullCal.destroy();
}
var g11n = controller.g11n;
apptFullCal.init({
data: {},
defaultDate: controller.defaultDate,
showMonth: true,
showWeekNav: true,
allowEditing: false,
calElem: $("#myCalendar"),
showWknd: apptPortlet.showWeekends,
updateWknd: apptPortlet.updateSettings,
g11n: controller.g11n,
listViewPageSize: apptPortlet.listViewPageSize,
noApptMesg: controller.g11n.noSignedUpAppts,
rerender: function () {
apptFullCal.reload();
$("#myNotifications").apptNotification('render');
},
editEvent: function(appt, isClick, revertFunc, rerenderAction) {
var resource = {};
if (appt.resourceId != '' && appt.resourceEdit)
resource = apptPortlet.getResource(appt.resourceId);
$.apptDetails({ controller: controller, appointment: appt, g11n: controller.g11n, resource: resource, canAddAttendees: apptPortlet.canAddAttendees, rerender: function () { rerenderAction(); $("#myNotifications").apptNotification('render'); } });
},
readonlySources: function (viewName) { return viewName == 'month' ? [{ url: 'rpc/appointmentsinfo/getcalendaraggregate' }] : [{ url: 'api/calendarevents?fullCalendar=true&filter=conflictable' }]; },
postApptRender: function (appt, elem, view) {
if (appt.isReadOnly) return;
var item = elem.find('.fc-content');
item.html(appt.isOwner ? appt.attendeeInfo : (appt.resourceId != '' ? appt.resourceName : appt.owner));
if (view.name != 'month') {
item.append($("<span class='fc-cus-event-info'>").html(appt.title));
}
if (appt.id == controller.apptId) {
controller.apptId = '';
apptFullCal.triggerClick(appt);
}
if (appt.newCommentCount > 0) {
var ctr = view.name == 'list' ? elem : item;
var comTxt = $("<span class='fc-comment'>").append($("<i class='fa fa-comment'/>")).append(view.name == 'list' ? stringUtil.format((appt.newCommentCount == 1 ? controller.g11n.newComment : controller.g11n.newComments), appt.newCommentCount) : '');
ctr.prepend(comTxt);
}
},
changeView: function(viewName) {
if (viewName == 'list')
$(".apptKeyInfo.otherEvents").hide();
else
$(".apptKeyInfo.otherEvents").show();
if (viewName == 'month')
$(".apptKeyInfo.otherEvents").removeClass("inactiveItem");
else
$(".apptKeyInfo.otherEvents").addClass("inactiveItem");
bindHelpText(g11n, viewName == 'list' ? '2' : (viewName == 'month' ? 0 : 1));
}
}, 'month');
bindHelpText(g11n, 0);
$("#myNotifications").apptNotification({
g11n: apptPortlet.g11n,
baseUrl: apptPortlet.baseUrl,
renderFullTxt: true,
callback: function(apptInfo) {
var date = moment(apptInfo.start);
if ((date.weekday() == 0 || date.weekday() == 6) && apptPortlet.showWeekends == false)
apptFullCal.showWeekends($("#myCalendar"), true, date);
controller.set('apptId', apptInfo.id);
apptFullCal.gotToDate('agendaDay', date);
}
});
$("#peopleChooser").peopleChooser({
peopleSource: 'rpc/appointmentsInfo/GetUserSearch',
watermark: controller.g11n.watermark,
onSelect: function(person) {
if (person.isResource)
controller.transitionTo('scheduleResource', person.id);
else
controller.transitionTo('userSchedule', { id: person.id, imgUrl: person.imgUrl, name: { full: person.fullName } });
}
});
if (controller.canManageSettings || controller.canAdmin)
$(".permissionAction").click(function() { ApptApp.history = { name: '', view: 'main', isSchedule: false }; });
var schLinkAction = this.bindScheduleLink;
$(".permHosts").hide();
if (apptPortlet.canManageSettings && (apptPortlet.apptSetting == 2 || (apptPortlet.apptSetting == 3 && apptPortlet.resourceId.length > 0))) {
var resource = apptPortlet.apptSetting == 2 ? null : apptPortlet.getResource(apptPortlet.resourceId);
if (apptPortlet.apptSetting == 2 || (resource != null && resource.canEdit)) {
$(".permHosts").show().find('a').html("<i class='fa fa-group'></i>" + (apptPortlet.apptSetting == 2 ? g11n.apptHosts : g11n.managersAndHosts))
.unbind('click').click(function(e) {
e.preventDefault();
$.apptManageHosts({
g11n: controller.g11n,
resource: resource,
isResource: resource != null,
portletId: apptPortlet.portletId,
onSave: function(res) {
if (apptPortlet.apptSetting == 2) {
apptPortlet.hosts = res;
schLinkAction(controller);
} else {
apptPortlet.hosts = res.hosts;
}
}});
});
}
}
if (controller.showMySchedule) {
$(".mySchedule").hide();
this.bindResourceInfo(controller);
} else {
$(".mySchedule").show();
}
this.bindSidebar(controller);
},
bindSidebar: function(controller) {
var campus = $(".campusWrapper").hide();
var myfac = $(".myFacWrapper").hide();
$.get('rpc/appointmentsinfo/getsidebarinfo/', function (sidebarInfo) {
if (sidebarInfo == null) return;
if (sidebarInfo.campusResources != null) {
var ul = campus.find('#campusResources').empty();
for (var key in sidebarInfo.campusResources) {
ul.append($("<li>").append($("<a href='#' class='activeItem apptStrong'>").data('id', key).html(sidebarInfo.campusResources[key]).click(function (e) { e.preventDefault(); controller.transitionTo('scheduleResource', $(this).data('id')); })));
}
if (ul.find('li').length > 0)
campus.show();
}
if (sidebarInfo.currentFaculty != null && sidebarInfo.currentFaculty.length > 0) {
myfac.show();
var container = myfac.find(".myFacUsers").empty();
$.each(sidebarInfo.currentFaculty, function (i, fac) {
var div = $("<div class='myFac'>");
div.append($("<a class='activeItem apptStrong'>").html(fac.name).click(function (e) { e.preventDefault(); controller.transitionTo('userSchedule', { id: fac.id, imgUrl: fac.imgUrl, name: { full: fac.name } }); }));
if (!fac.hasAvailAppts)
div.append($("<span class='pc-details'>").html(controller.g11n.noAppt));
$.each(fac.sections, function (j, sec) {
div.append($("<div class='inactiveItem itemInfo'>").html(sec));
});
container.append(div);
});
}
});
},
processRedirect: function (controller) {
if (apptPortlet.name == null) {
controller.transitionTo("noAccess");
return true;
}
if (controller.isDirect) return false;
var date = controller.defaultDate != null && controller.defaultDate != '' ? moment(controller.defaultDate).format('MM-DD-YYYY') : null;
if ((apptPortlet.resourceId != '' || apptPortlet.apptSetting == 2) && !controller.showMySchedule) {
var url = apptPortlet.apptSetting == 2 ? 'scheduleSpecific' : 'schedule';
if (controller.apptId != '' || date != null) {
controller.transitionTo(url + "Id", (controller.apptId + '|' + date));
}
else
controller.transitionTo(url);
return true;
}
else if (apptPortlet.apptSetting == 1 && !controller.showMySchedule && apptPortlet.hosts != null && apptPortlet.hosts.length > 0) {
var host = apptPortlet.hosts[0];
if (controller.apptId != '' || date != null) {
controller.transitionTo('userScheduleId', host.id + '_' + ((controller.apptId == '' ? ' ' : controller.apptId) + '|' + date));
}
else
controller.transitionTo('userSchedule', { id: host.id, imgUrl: host.imgUrl, name: { full: host.name } });
return true;
}
return false;
},
bindScheduleLink: function (controller) {
var showLink = false;
if (apptPortlet.resourceId != '') {
var resource = apptPortlet.getResource(apptPortlet.resourceId);
$(".scheduleLink").html(stringUtil.format(controller.g11n.bckToSchedule, resource.name)).unbind('click').click(function (e) { e.preventDefault(); controller.transitionTo('schedule'); });
showLink = true;
}
else if (apptPortlet.apptSetting == 1) {
var host = apptPortlet.hosts[0];
$(".scheduleLink").html(stringUtil.format(controller.g11n.bckToSchedule, host.name)).unbind('click').click(function (e) { e.preventDefault(); controller.transitionTo('userSchedule', { id: host.id, imgUrl: host.imgUrl, name: { full: host.name } }); });
showLink = true;
}
else if (apptPortlet.apptSetting == 2) {
var names = '';
var userCount = apptPortlet.hosts.length;
for (var i = 0; i < userCount; i++) {
names += " " + apptPortlet.hosts[i].name;
if (userCount > 1 && (userCount - i != 1)) {
names += userCount - i == 2 ? ", " + controller.g11n.and : ",";
}
}
$(".scheduleLink").html(stringUtil.format(controller.g11n.bckToSchedule, names)).unbind('click').click(function (e) { e.preventDefault(); controller.transitionTo('scheduleSpecific'); });
showLink = true;
}
if(showLink)
$(".bckToSchedule").show();
else
$(".bckToSchedule").hide();
},
bindResourceInfo: function (controller) {
this.bindScheduleLink(controller);
var ul = $("#userSchedules").empty();
if (apptPortlet.ableToEdit) {
ul.append($("<li>").append($("<a href='#' class='activeItem apptStrong'>").html(apptPortlet.name.full).click(function (e) { e.preventDefault(); controller.transitionTo('userSchedule', apptPortlet.id); })));
$(".rg-sidebar").show();
}
$.get('rpc/appointmentsInfo/GetMySchedules/', function (schedules) {
if (schedules == null) return;
for (var key in schedules) {
ul.append($("<li>").append($("<a href='#' class='activeItem apptStrong'>").data('id',key).html(schedules[key]).click(function (e) { e.preventDefault(); controller.transitionTo('scheduleResource', $(this).data('id')); })));
}
if (schedules.length > 0)
$(".rg-sidebar").show();
});
}
});
function bindHelpText(g11n, screen) {
$(".apptHelpWrapper").appointmentHelp({ g11n: g11n, isHost: apptPortlet.ableToEdit, portletId:apptPortlet.portletId, screen: screen });
}
}
I briefly used Ember 1.7 but only ever with ember-cli. Anyway, I can point out a couple of things I am pretty sure that you aren't doing correctly. First off:
I changed all instances of the code where it was using
Ember.View.extend to Ember.Controller.extend
This does not make any sense. These are not comparable. If you meant Ember.Component that would make more sense but there's still not a 1:1 similarity. I would recommend studying the Ember.View docs from 1.7 as well as that for components and controllers. You should also give a the Ember.View deprecation guide a read through. The recommended upgrade path is to convert views to components, but components are isolated contexts unlike Ember.View. I think you will have to pass the controller to the Ember.Component instance (would be better to pass the properties needed). Since Ember.Component extends Ember.View, you might not be able to pass via the controller property directly (I don't know if this would be problematic but I would probably avoid).
Accessing data via {{view.someProp}} or {{controller.thisThing}} can nearly always be replaced by proper use of data passing and block params. See the guide on differences in yielded blocks for a complete example of using block params over the {{view}} keyword.
I would recommend going all the way through the deprecations guide to understand what needs to change.
Right now, the following command operates automatically. I am wondering how I can create a button that will enable the getUserMedia command, and subsequently display the text below.
navigator.getUserMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
if (navigator.getUserMedia) {
navigator.getUserMedia({
video:true, audio: true
},
function(stream) {
// returns true if any tracks have active state of true
var result = stream.getVideoTracks().some(function(track) {
return track.enabled && track.readyState === 'live';
});
if (!result) {
$('.abada').append('<h4>2000(works)</h4>');
} else {
$('.abada').append('<h4>2000(works)</h4>');
}
},
function(e) {
$('.abada').append('<h4>2001(does not work)</h4>');
});
}
The key here is to use an onclick function. So we can use:
onclick="function" as an attribute for the HTML tag,
var element = document.querySelector("#button");
element.addEventListener("click", function);,
element.onclick = function; are both acceptable in JavaScript,
var element = $("#button");
element.on("click", function);,
element.click(function); in jQuery.
Shown below:
Using jQuery:
$('#button').on("click", function () {
if (navigator.getUserMedia) {
navigator.getUserMedia({
video: true, audio: true
}, function(stream) {
// returns true if any tracks have active state of true
var result = stream.getVideoTracks().some(function(track) {
return track.enabled && track.readyState === 'live';
});
if (!result) {
$('.abada').append('<h4>2000(works)</h4>');
} else {
$('.abada').append('<h4>2000(works)</h4>');
}
}, function(e) { $('.abada').append('<h4>2001(does not work)</h4>');
});
);
HTML:
<button id="button">Click Me For Streaming</button>
How can I handle the back button action on Ionic 2?
I want to be able to know what to do depending on which page is being shown to the user.
I didn't find a good answer to this question, but after a while I figured it out myself a way to do it. I'm gonna share with you all.
Thanks
Here's how I did it:
In every Page component, I created a function called backButtonAction(), which will execute custom code for every page.
Code:
import { Component } from '#angular/core';
import { Platform, NavController, ModalController } from 'ionic-angular';
import { DetailsModal } from './details';
#Component({
selector: 'page-appointments',
templateUrl: 'appointments.html'
})
export class AppointmentsPage {
modal: any;
constructor(private modalCtrl: ModalController, public navCtrl: NavController, public platform: Platform) {
// initialize your page here
}
backButtonAction(){
/* checks if modal is open */
if(this.modal && this.modal.index === 0) {
/* closes modal */
this.modal.dismiss();
} else {
/* exits the app, since this is the main/first tab */
this.platform.exitApp();
// this.navCtrl.setRoot(AnotherPage); <-- if you wanted to go to another page
}
}
openDetails(appointment){
this.modal = this.modalCtrl.create(DetailsModal, {appointment: appointment});
this.modal.present();
}
}
And in the app.component.ts, I used the platform.registerBackButtonAction method to register a callback that will be called everytime the back button is clicked. Inside it I check if the function backButtonAction exists in the current page and call it, if it doesn't exists, just go to the main/first tab.
One could simplify this if they didn't need to perform customized actions for every page. You could just pop or exit the app.
I did it this way because I needed to check if the modal was open on this particular page.
Code:
platform.registerBackButtonAction(() => {
let nav = app.getActiveNav();
let activeView: ViewController = nav.getActive();
if(activeView != null){
if(nav.canGoBack()) {
nav.pop();
}else if (typeof activeView.instance.backButtonAction === 'function')
activeView.instance.backButtonAction();
else nav.parent.select(0); // goes to the first tab
}
});
if the current page is the first tab, the app closes (as defined in the backButtonAction method).
Ionic Latest version 3.xx app.component.ts file import { Platform, Nav, Config, ToastController} from 'ionic-angular';
constructor(public toastCtrl: ToastController,public platform: Platform) {
platform.ready().then(() => {
//back button handle
//Registration of push in Android and Windows Phone
var lastTimeBackPress=0;
var timePeriodToExit=2000;
platform.registerBackButtonAction(() => {
// get current active page
let view = this.nav.getActive();
if(view.component.name=="TabsPage"){
//Double check to exit app
if(new Date().getTime() - lastTimeBackPress < timePeriodToExit){
this.platform.exitApp(); //Exit from app
}else{
let toast = this.toastCtrl.create({
message: 'Press back again to exit App?',
duration: 3000,
position: 'bottom'
});
toast.present();
lastTimeBackPress=new Date().getTime();
}
}else{
// go to previous page
this.nav.pop({});
}
});
});
}
I used answers from here and other sources to accomplish what I needed.
I noticed that when you build the application for production (--prod) this approach doesn't work, because of JS uglifying and simplifying:
this.nav.getActive().name == 'PageOne'
Because of that, I use next in the "if" statement:
view.instance instanceof PageOne
So the final code looks like this:
this.platform.ready().then(() => {
//Back button handling
var lastTimeBackPress = 0;
var timePeriodToExit = 2000;
this.platform.registerBackButtonAction(() => {
// get current active page
let view = this.nav.getActive();
if (view.instance instanceof PageOne) {
if (new Date().getTime() - lastTimeBackPress < timePeriodToExit) {
this.platform.exitApp(); //Exit from app
} else {
let toast = this.toastCtrl.create({
message: 'Tap Back again to close the application.',
duration: 2000,
position: 'bottom',
});
toast.present();
lastTimeBackPress = new Date().getTime();
}
} else if (view.instance instanceof PageTwo || view.instance instanceof PageThree) {
this.openPage(this.pages[0]);
} else {
this.nav.pop({}); // go to previous page
}
});
});
As per Ionic 2 RC.4 documentation from here:
You can use registerBackButtonAction(callback, priority) method of Platform API to register the action on back button press.
The back button event is triggered when the user presses the native platform’s back button, also referred to as the “hardware” back button. This event is only used within Cordova apps running on Android and Windows platforms. This event is not fired on iOS since iOS doesn’t come with a hardware back button in the same sense an Android or Windows device does.
Registering a hardware back button action and setting a priority allows apps to control which action should be called when the hardware back button is pressed. This method decides which of the registered back button actions has the highest priority and should be called.
Parameters :
callback : Function to be called when the back button is pressed, if this registered action has the highest priority.
priority : Number to set the priority for this action. Only the highest priority will execute. Defaults to 0
Returns: Function : A function that, when called, will un-register the back button action.
Actually ionViewWillLeave works better in my case.
Here are the official docs about navigating lifecycle
I was able to accomplish this in the event that we are simply setting root pages...
import {Component, ViewChild, Injector} from '#angular/core';
import {Platform, MenuController, Nav, App, IonicApp, NavController} from 'ionic-angular';
import {StatusBar} from '#ionic-native/status-bar';
import {SplashScreen} from '#ionic-native/splash-screen';
import {InvitesPage} from "../pages/invites/invites";
import {RewardsPage} from "../pages/rewards/rewards";
import {ConnectionsPage} from "../pages/connections/connections";
import {MessagesPage} from "../pages/messages/messages";
import {ResourcesPage} from "../pages/resources/resources";
import {SignoutPage} from "../pages/signout/signout";
import {DashboardPage} from "../pages/dashboard/dashboard";
import {AccountPage} from "../pages/account/account";
import {HomePage} from "../pages/home/home";
import {TriviaPage} from "../pages/trivia/trivia";
import {Events} from "ionic-angular/util/events";
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: NavController;
// make HelloIonicPage the root (or first) page
public rootPage: any; //if logged in, go to dashboard.
public pages: Array<{title: string, component: any}>;
public user: any;
public routeHistory: Array<any>;
constructor(public platform: Platform,
public menu: MenuController,
public statusBar: StatusBar,
public splashScreen: SplashScreen,
private _app: App,
private _ionicApp: IonicApp,
private _menu: MenuController,
protected injector: Injector,
public _events: Events) {
this.initializeApp();
// set our app's pages
this.pages = [
{title: 'My Account', component: AccountPage},
{title: 'Dashboard', component: DashboardPage},
{title: 'Invites', component: InvitesPage},
{title: 'Rewards', component: RewardsPage},
{title: 'Connections', component: ConnectionsPage},
{title: 'Messages', component: MessagesPage},
{title: 'Resources', component: ResourcesPage},
{title: 'Trivia', component: TriviaPage},
{title: 'Sign Out', component: SignoutPage}
];
this.routeHistory = [];
this.user = {firstName: ''};
}
initializeApp() {
this.platform.ready().then(() => {
this._setupBrowserBackButtonBehavior();
let self = this;
if (sessionStorage.getItem('user')) {
this.user = JSON.parse(sessionStorage.getItem('user'));
self.rootPage = TriviaPage;
} else {
self.rootPage = HomePage;
}
this.routeHistory.push(self.rootPage);
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
openPage(page) {
// close the menu when clicking a link from the menu
this.menu.close();
// navigate to the new page if it is not the current page
this.nav.setRoot(page.component);
//store route history
this.routeHistory.push(page.component);
}
private _setupBrowserBackButtonBehavior() {
// Register browser back button action(s)
window.onpopstate = (evt) => {
// Close menu if open
if (this._menu.isOpen()) {
this._menu.close();
return;
}
// Close any active modals or overlays
let activePortal = this._ionicApp._loadingPortal.getActive() ||
this._ionicApp._modalPortal.getActive() ||
this._ionicApp._toastPortal.getActive() ||
this._ionicApp._overlayPortal.getActive();
if (activePortal) {
activePortal.dismiss();
return;
}
if (this.routeHistory.length > 1) {
this.routeHistory.pop();
this.nav.setRoot(this.routeHistory[this.routeHistory.length - 1]);
}
};
// Fake browser history on each view enter
this._app.viewDidEnter.subscribe((app) => {
if (this.routeHistory.length > 1) {
history.pushState(null, null, "");
}
});
}
}
I found the Easiest way, just add the following code in app.component:
this.platform.registerBackButtonAction((event) => {
let activePortal = this.ionicApp._loadingPortal.getActive() ||
this.ionicApp._modalPortal.getActive() ||
this.ionicApp._toastPortal.getActive() ||
this.ionicApp._overlayPortal.getActive();
if(activePortal && activePortal.index === 0) {
/* closes modal */
activePortal.dismiss();
} else {
if(this.nav.getActive().name == 'Homepage') { // your homepage
this.platform.exitApp();
}
else {
if(this.nav.canGoBack())
this.nav.pop();
this.nav.setRoot(Homepage);
}
}
},101);
That's it! No need to add extra code on every page!
Best practice solution after long search .
it works 100%, and tested it in real device
this.Platform.registerBackButtonAction(() => {
// try to dismiss any popup or modal
console.log("Back button action called");
let activePortal = this.ionicApp._loadingPortal.getActive() ||
this.ionicApp._modalPortal.getActive() ||
this.ionicApp._toastPortal.getActive() ||
this.ionicApp._overlayPortal.getActive();
if (activePortal) {
// ready = false;
activePortal.dismiss();
activePortal.onDidDismiss(() => { });
console.log("handled with portal");
return;
}
// try to close the menue
if(this.MenuController.isOpen()){
this.closeMenu();
return;
}
else if(this.nav.canGoBack()){
this.nav.pop();
return;
}else{
let activePage = this.nav.getActive().instance;
let whitelistPages = [LoginPage, HomePage];
// if current page is not in whitelistPage
// then back to home or login page first
if (whitelistPages.indexOf(activePage.constructor) < 0) {
this.nav.setRoot(this.userLoggedIn ? HomePage : LoginPage);
return;
}else if(whitelistPages.indexOf(activePage.constructor) > 0){
this.AppUtilities.showConfirm("Exit","Are you want to exist the app ? ").subscribe(
()=>{
this.Platform.exitApp();
},
()=>{}
)
}else{
console.error('cannot handel back button')
}
}
});
I have a slight different approach compare with #amr abdulaziz. Im using the setTimeout to control back or exit. Hope this would give another option for implement the back button.
initBackButtonBehaviour() {
this.platform.registerBackButtonAction(() => {
console.log("Back button pressed");
if (this.readyToExit) {
this.platform.exitApp();
return;
}
let activePortal = this.ionicApp._loadingPortal.getActive() ||
this.ionicApp._modalPortal.getActive() ||
this.ionicApp._toastPortal.getActive() ||
this.ionicApp._overlayPortal.getActive();
if (activePortal) {
activePortal.dismiss();
activePortal.onDidDismiss(() => { });
return; // stop any further action after closing any pop up modal or overlay
}
if (this.menuCtrl.isOpen()) {
this.menuCtrl.close();
return; // stop any further action after menu closed
}
else if (this.nav.canGoBack()) {
this.nav.pop();
return; // stop any further action after navigation pop
}
else {
let activePage = this.nav.getActive().instance;
let whiteListPages = [HomePage];
// if current page is not in whitelistPage
// then back to home or login page first
if (whiteListPages.indexOf(activePage.constructor) < 0) {
this.nav.setRoot(HomePage);
return;
} else if (whiteListPages.indexOf(activePage.constructor) >= 0) {
this.utils.showToast('Press back button again to exit', 1500);
this.readyToExit = true;
setTimeout(() => {
this.readyToExit = false;
}, 1500);
} else {
console.error('cannot handle back button');
}
}
}, 101);
I have Researched Many Things for back button handle Finally I Found a Good Solution for ionic latest Version 3.xx
//Check Hardware Back Button Double Tap to Exit And Close Modal On Hardware Back
let lastTimeBackPress = 0;
let timePeriodToExit = 2000;
this.platform.registerBackButtonAction(() => {
let activePortal = this.ionicApp._loadingPortal.getActive() || // Close If Any Loader Active
this.ionicApp._modalPortal.getActive() || // Close If Any Modal Active
this.ionicApp._overlayPortal.getActive(); // Close If Any Overlay Active
if (activePortal) {
activePortal.dismiss();
}
else if(this.nav.canGoBack()){
this.nav.pop();
}else{
//Double check to exit app
if (new Date().getTime() - lastTimeBackPress < timePeriodToExit) {
this.platform.exitApp(); //Exit from app
} else {
this.toast.create("Press back button again to exit");
lastTimeBackPress = new Date().getTime();
}
}
});
In Ionic 3 Lazy Loading, I never felt the need of Handling back behavior of browser where as for platform.is('cordova') I have created following method which handles all back scenarios:
// If a view controller is loaded. Just dismiss it.
let nav = this.app.getActiveNav();
let activePortal = this._ionicApp._loadingPortal.getActive() ||
this._ionicApp._modalPortal.getActive() ||
this._ionicApp._toastPortal.getActive() ||
this._ionicApp._overlayPortal.getActive();
if(activePortal && activePortal.index === 0) {
/* closes modal */
activePortal.dismiss();
return;
}
// If a state is pushed: Pop it.
if (this.nav.canGoBack()) {
this.nav.pop();
return;
} else
// Else If its a tabs page:
if (this.nav.getActiveChildNav()) {
const tabs: Tabs = this.nav.getActiveChildNav();
const currentTab = tabs.getActiveChildNavs()[0];
// If any page is pushed inside the current tab: Pop it
if(currentTab.canGoBack()) {
currentTab.pop();
return;
}
else
// If home tab is not selected then select it.
if(tabs.getIndex(currentTab) !=0){
tabs.select(0);
return;
}
}
else
// If a menu is open: close it.
if (this.menu.isOpen()) {
this.menu.close();
return;
}
if (this.exitApp) {
this.platform.exitApp();
return;
}
this.exitApp = true;
const toast = this.toastCtrl.create({
message: this.exitMessage || 'press again to exit',
duration: 4000,
position: 'bottom',
cssClass: 'exit-toastr',
});
toast.present();
setTimeout(() => {
this.exitApp = false;
}, 2000);
you can try this functions :
registerBackButton() {
this.platform.registerBackButtonAction(() => {
if (this.menu.isOpen()) {
console.log("Menu is open!", "loggedInMenu");
this.menu.close();
console.log("this.menu.isOpen(): " + JSON.stringify(this.menu.isOpen()));
return;
}
console.log("Checking for other pages");
let checkHomePage = true;
let max = Globals.navCtrls.length;
for (let i = 0; i < Globals.navCtrls.length; i++) {
let n = Globals.navCtrls[i];
if (n) {
if (n.canGoBack()) {
console.log("Breaking the loop i: " + JSON.stringify(i));
let navParams = n.getActive().getNavParams();
if (navParams) {
console.log("navParams exists");
let resolve = navParams.get("resolve");
if (resolve) {
n.pop().then(() => resolve({}));
} else {
n.pop();
}
} else {
n.pop();
}
checkHomePage = false;
return;
}
} else console.log("n was null!");
}
if (this.nav.getActive().instance instanceof TabsPage && !this.nav.canGoBack()) {
let popPageVal = this.backbuttonService.popPage();
console.log("popPageVal: " + JSON.stringify(popPageVal));
if (popPageVal >= 0) {
console.log("Switching the tab to: ", popPageVal);
this.switchTab(popPageVal);
} else {
console.log("Last page is HomePage");
if (this.alert) {
this.alert.dismiss();
this.alert = null;
} else {
this.showAlert();
}
}
} else {
console.log("Last page is not HomePage");
if (this.nav.canGoBack()) {
console.log("We can go back!");
this.nav.pop();
}
}
});
}
showAlert() {
this.alert = this.alertController.create({
title: "Exit?",
message: "Are you sure you want to exit?",
buttons: [
{
text: "Cancel",
role: "cancel",
handler: () => {
this.alert = null;
}
},
{
text: "Exit",
handler: () => {
this.platform.exitApp();
}
}
]
});
this.alert.present();
}
switchTab(tabIndex) {
if (Globals.tabs && tabIndex >= 0) {
console.log("Switch condition met");
Globals.tabIndex = tabIndex;
Globals.tabs.select(tabIndex);
Globals.tabs.selectedIndex = tabIndex;
}
}
I hope its works with you.
Hi I have a doubt in how using promises in Ember. Basically I want to setup the attributes of my controller before running what is next the promise, my code:
export default Ember.Controller.extend({
min: null,
max: null,
isValid: Ember.computed(function(){
var self = this;
var result = new Ember.RSVP.Promise(function(resolve, reject){
var url = "..."
Ember.$.getJSON(url).then(function(data){
if (data[0] === 0){
self.set('errorMessage', "It is a free book");
return false;
} else if (data[0] > 2000){
self.set('errorMessage', "You are poor, go for next");
return false;
} else {
console.log(data); # line 2 of console result
self.set('min', data[1]);
self.set('max', data[2]);
return true;
}
});
});
}
return result;
}),
actions: {
save: function(){
if (this.get('isValid')){
console.log('save action is fired. -- VALID' + '--' + this.get('maxPrice')); # line 1 of console result
} else {
console.log('save action is fired. -- INVALID');
}
}
}
});
The thing is in the third if statement, the code in save action is running before setting the attributes min and max inside the promise. Result in console:
> save action is fired. -- VALID--null
> [9, 1, 20]
Any idea how to set the values before the action go ahead?
Thanks,
There isn't much point in wrapping the jquery promise with another promise. Just use the jquery promise, return it, and then since this is an async world, you need to then off of that promise.
export default Ember.Controller.extend({
min: null,
max: null,
checkValid: function(url) {
var self = this;
return Ember.$.getJSON(url).then(function(data) {
if (data[0] === 0) {
self.set('errorMessage', "It is a free book");
return false;
} else if (data[0] > 2000) {
self.set('errorMessage', "You are poor, go for next");
return false;
} else {
console.log(data);#
line 2 of console result
self.set('min', data[1]);
self.set('max', data[2]);
return true;
}
});
},
actions: {
save: function() {
var self = this;
var url = ...;
this.checkValid(url).then(function(result) {
if (result) {
console.log('save action is fired. -- VALID' + '--' + self.get('maxPrice'));
} else {
console.log('save action is fired. -- INVALID');
}
});
}
}
});
Additionally when you're using the RSVP promise, you need to actually call resolve/reject for the promise to ever resolve or reject. Here's a quick video on how to use RSVP promises: https://www.youtube.com/watch?v=8WXgm4_V85E
I'm trying to make a link stay 'active' on multiple routes, such as /#/users and /#/user.
Any ideas?
You can reopen Ember's LinkView and do something like this (allows currentWhen to contain space delimited values)
Ember.LinkView.reopen({
active: function() {
// we allow link-to's currentWhen to specify multiple routes
// so we need to check each one of them
var loadedParams = Ember.get(this, 'loadedParams');
var currentWhen = this['current-when'] || this.currentWhen;
currentWhen = currentWhen || (loadedParams && loadedParams.targetRouteName);
if (currentWhen && currentWhen.indexOf(' ') >= 0) {
var currents = currentWhen.split(' ');
router = Ember.get(this, 'router');
for (var i = 0; i < currents.length; i++) {
var isActive = router.isActive.apply(router, [currents[i]]);
if (isActive)
return isActive;
}
return false;
}
return this._super();
}.property('resolvedParams', 'routerArgs')
});
This is a total hack, but it works in Ember 1.0.0. To make links to users stay active when the user route is active:
App.UserRoute = Ember.Route.extend({
activate: function() {
setTimeout(function() {
$('[href="#/users"]').addClass("active");
}, 0);
}
});