slideChangeStart event does not fire when I swipe - ionic-framework

In my Ionic 1.3.1 app I am using the ion-slides component to display sections of a questionnaire:
<ion-slides options="vm.options" slider="data.slider">
<ion-slide-page ng-repeat="s in ::vm.sections">
<div class="bar bar-header bar-positive">
<button class="button button-small button-icon icon ion-chevron-left"
ng-click="vm.previous()"
ng-show="::$index !== 0"></button>
<h1 class="title">{{::s.text}}</h1>
<button class="button button-small button-icon icon ion-chevron-right"
ng-click="vm.next()"
ng-show="::($index + 1) < vm.sections.length"></button>
</div>
<ion-content class="has-header">
<div ng-if="s.include" ng-show="s.show">
<!--My content-->
</div>
</ion-content>
</ion-slide-page>
</ion-slides>
In my controller I listen for the slideChangeStart:
$scope.$on("$ionicSlides.slideChangeStart", function (event, data) {
alert('slideChangeStart');
vm.activeIndex = slider.activeIndex;
vm.propertySurvey.CurrentSectionIndex = vm.activeIndex;
//include is used on an ng-if so elements are removed from the dom
//and the number of watchers is reduced
//also, the slider displays the contents of the
//previous slide unless they are explicitly hidden
vm.sections[slider.previousIndex].include = false;
vm.sections[slider.previousIndex].show = false;
initialiseQuestions(vm.activeIndex);
});
When I click on my previous and next buttons, which call slider.slidePrev() or slider.slideNext(), the slideChangeStart event fires - and I see the alert. But if I slide the page using a gesture - the header changes as I expect but the event does not fire. I've tried switching the swipe off in my options without success (not my preferred solution anyway):
vm.options = {
noSwiping: true,
effect: 'fade'
}
UPDATE
I tried using slideChangeEnd but that event isn't firing either.
So I moved all my code into a single gotoSlide(index) method that I call from my next, previous and pagerClick methods - so that I don't rely on the ion-slides event.
And I added Ionic on-swipe directives to my ion-slide-page component to see if they would work:
<ion-slide-page ng-repeat="s in ::vm.sections"
on-swipe-left="vm.next()"
on-swipe-right="vm.previous()">
That made the swipe work in the Ripple emulator (where it wasn't working at all before), but it still doesn't work on any of my android devices. Again, the swipe events don't seem to fire.
I am using the Crosswalk plugin

The problem goes away if I remove the effect option. So this works:
vm.options = {
noSwiping: false,
speed: 300
}
And, since these are the default values anyway, I ended up removing the options object altogether.
NB replacing 'fade' with 'slide' did not work
Reference:
http://ionicframework.com/docs/api/directive/ionSlides/

I had a similar problem, and the above answer did not work for me, so I thought I'd share what did:
Per the Swiper API (which underlies ion-slides), you can add event based call backs directly to the widget.
The following worked for me (assuming the scope.slider object...)
//the 'slideChangeStart' event has a slider argument in its callback
scope.slider.on('slideChangeStart', function(slider) {
//do stuff - maybe you want to store the activeIndex
scope.indexIReallyCareAbout = slider.activeIndex;
});
The Swiper API gives a list of events that can named in the 'on' callback function under the header Callbacks

Related

Ionic 4 infinite-scroll initial scroll position

I'm trying to make a dual infinite scroll case when new items are added as the user scrolls either all the way up or all the way down.
<ion-infinite-scroll (ionInfinite)="scrolledUp($event)" position="top">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
<ion-list>
<ion-item *ngFor="let item of items">
<!-- item content -->
<ion-item>
</ion-list>
<ion-infinite-scroll (ionInfinite)="scrolledDown($event)" position="bottom">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
The problem is that the list opens initially at the bottom. Of course I can scrollIntoView the first item on ionViewDidEnter but it wouldn't look nice and would confuse the users.
I noticed it's the one with position=top that is responsible for the initial position at the bottom.
Is there a way to set the initial scroll position for the ion-infinite-scroll without having to explicitly scroll the list to top?
As I initially suspected the infinite scroll component does force a scroll to bottom if position is set to top. It occurs in the componentDidLoad method as shown below:
async componentDidLoad() {
const contentEl = this.el.closest('ion-content');
if (contentEl) {
await contentEl.componentOnReady();
this.scrollEl = await contentEl.getScrollElement();
}
this.thresholdChanged();
this.disabledChanged();
if (this.position === 'top') {
writeTask(() => {
if (this.scrollEl) {
this.scrollEl.scrollTop = this.scrollEl.scrollHeight - this.scrollEl.clientHeight;
}
});
}
}
As the component provides no means to intervene in this behavior and without an option to extend or otherwise customize the default stencil components, I turned to a somewhat hacky way to achieve the desired behavior.
Therefore, you first need to remove the position=top part from the ion-infinite-scroll declaration in your view so that the scroll part of the above code won't trigger. You would also provide a name for the ion-infinite-scroll so that it can be referenced from your page class.
<ion-infinite-scroll #top (ionInfinite)="scrolledUp($event)">
Then you make a reference to #top above in your page.
#ViewChild('top') topScroller;
Then in the ionViewDidEnter life cycle hook, you can set the position for the scroller as top.
setTimeout(() => {
this.topScroller.position = 'top';
}, 200);
It has to be in a time out because the topScroller variable is still not assigned when ionViewDidEnter is called and the next life cycle hook is ionViewWillLeave which won't be that useful for our purpose.

How to insert a html code after the page is rendered

I am trying to insert a piece of html code (ion-header-bar) after i click on login button . I need this code to be added on top of all the modules. As it is a same piece of code in all the modules, is there anyway to achieve it by writing it in a single place rather than adding the repeated code to all the modules.
EDIT I realize that the question did not clarify which version of ionic is used. This answer is only working for ionic 2 and ionic 3 (and possibly later versions) - ionic 1 will work differently
if you follow the typical structure of an Ionic 2 or 3 Project (with an App.component using <ion-nav>) you would just put the <ion-header-bar> next to your <ion-nav> with an *ngIf:
/// src/app/app.html
<ion-header-bar *ngIf="isUserLoggedIn()">This only shows when is UserLoggedIn() returns true</ion-header-bar>
<ion-nav [root]="rootPage"></ion-nav>
And inside your app.component.ts you just implement the function used by the *ngIf
/// src/app/app.component.ts
#Component({
templateUrl: 'app.html'
})
export class MyApp {
public rootPage: any = MenuPage;
public isUserLoggedIn(): boolean {
// TODO: Implement this to return true when the user is logged in
}
}
there are many ways to do this - but this is about the simplest I can think of right now, given the limited information you provided.
I created a codepen for this:
https://codepen.io/anon/pen/aEYBmL
In your HTML add ng-if to your header bar and give it a function that just returns true when the user is logged in:
<html>
(...)
<body ng-controller="MyCtrl">
<ion-header-bar class="bar-positive" ng-if="isUserLoggedIn()">
<h1 class="title">this only shows if isUserLoggedIn() returns true</h1>
</ion-header-bar>
<ion-content>
<h1 style="margin-top: 56px">Here is your other content!</h1>
</ion-content>
</body>
</html>
then attach that function or variable to your scope inside your controller:
angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope) {
$scope.isUserLoggedIn = () => {
// TODO Implement this
return true;
}
});
read about ng-if here:
https://docs.angularjs.org/api/ng/directive/ngIf
EDIT: Displaying a component on every screen:
Look at this codepen:
https://codepen.io/calendee/pen/ubzDq
here you have your ion-nav-navbar (which is simmilar to a ion-header-bar with a backbutton and a title) outside of ion-nav-bar, which makes it display all the time - without putting it everywhere
///This wont be replaced as its not inside the ion-nav-view. So its displayed on every screen.
<ion-nav-bar>
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
<ion-nav-view>
/// Everything in here will be replaced by your current navstack
</ion-nav-view>

Ionic 2 Updating Button Text and Event on Click

Sorry if this sounds very obvious but I am new to Ionic 2 / Angular 2. Upon submitting a form, I need to update the button text and click event, ie:
first click on button = submit form + update button text to "Next"
second click on button = trigger goToNext()
I managed to update the button text but not update the click event (to goToNext() ).
.html
<form (ngSubmit)="logForm(i)">
<ion-item>
<ion-input type="text" [(ngModel)]="form.userinput[i]" name="userinput[i]"></ion-input>
</ion-item>
<button ion-button block type="submit" (click)="setNext($event.target, 'Next')">Check</button>
</form>
.ts
setNext(element, text){
element.textContent = 'Next';
}
goToNext(){
// go to Next Page
}
Ideally you change your design a bit to keep a variable that stores state of your 'Controller'. e.g. stores PageNumber. and then behave differently based on what page you are on. So I suggest change design a bit.
But to answer your current question without major change, you can bind the handler dynamically the same way you bind the text. then in the first handler, change the handler for the next click. the default values for handler and text will decide which one is going the be used initially
handler = this.setNext;
text = 'first text';
setNext(){
alert('handler1 called');
this.handler = this.goToNext;
this.text = 'other text';
}
goToNext(){
alert('second called');
// go to Next Page
}
and in your html you go like
<button ion-button block type="submit" (click)="handler()">{{text}}</button>
You can use n00b answer or something like this:
in html file:
<button ion-button block type="submit" (click)="check()">{{btn_txt}}</button>
in ts file:
btn_txt = 'Check';
check() {
if (this.btn_txt == 'Check') {
//do some logic
this.btn_txt = 'Next';
} else {
console.log('go to next page');
}
}

Ionic - text input in a modal crashes the app on ios

I have a text input field for my form that I am trying to fill out using a modal. But the app crashes on me. The problem occurs on the ios device. It doesn't occur on the web / ionic serve. I didn't test it on Android because I don't own a device. My other Modals work fine.
Repro Steps:
Tap on the modal item
Model Opens
Enter a value in the text field
4 Tap Save Button - The value is returned to the main controller
Tap again to edit the value - The app will hang
If Steps 1,2,3 are repeated by restarting the app and Done button on the device keyboard is tapped it also hangs.
Function Inside Main Controller:
// Code for my Modal Controller:
$ionicModal.fromTemplateUrl('templates/mymodal.html', function(modal) {
$scope.gmodalCtrl = modal;
}, {
scope: $scope,
animation: 'slide-in-left',
focusFirstInput: true
});
$scope.gmodalData = {"merchtype" : 'Please pick one'};
$scope.opengModal = function() {
$scope.gmodalCtrl.show();
};
MyApp.controller('MyController', function($scope) {
$scope.hideModal = function() {
$scope.gmodalCtrl.hide();
};
$scope.item = {};
$scope.doSomething = function(item) {
console.log('going with G Modal');
$scope.gmodalData.myrange = $scope.item.myrange;
console.log($scope.gmodalData.myrange);
console.log($scope.item.myrange);
$scope.gmodalCtrl.hide();
};
});
// HTML to Invoke Modal:
<label class="item"> <button class="button icon-right ion-ios7-arrow-right button-clear button-dark" ng-click="opengModal()">Merchant Name: {{gmodalData.myrange}} </button> </label>
// Modal HTML:
<input type="text" style="width: 250px" ng-model="item.myrange">
<button class="button button-block button-balanced" ng-click="doSomething()">Do it</button>
Output in the Xcode Debugger:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x176b75b0 V:|-(20)-[UIInputSetContainerView:0x176b5f70] (Names: '|':UITextEffectsWindow:0x176b3960 )>",
"<NSLayoutConstraint:0x176a3210 'UIInputWindowController-top' V:|-(0)-[UIInputSetContainerView:0x176b5f70] (Names: '|':UITextEffectsWindow:0x176b3960 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x176b75b0 V:|-(20)-[UIInputSetContainerView:0x176b5f70] (Names: '|':UITextEffectsWindow:0x176b3960 )>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
I get the same problem, my xCode is 7.2(7C68), ionic 1.7.12, cordova 5.1.1, iOS 9.2.
I fix this problem by adding another plugin.
Please checkout.
https://github.com/driftyco/ionic-plugin-keyboard
ionic plugin add ionic-plugin-keyboard
ionic build ios
then install this app with xCode, the button works.
Hope it will help you.

How to use Modal Pop up with Material Design Lite?

I recently started using the latest Desktop version of Google Material Design Lite, I figured it doesn't have a modal pop up and the team has not yet implemented it for the next release.
I have tried to include bootstrap model into it, but thats not working infect seems pretty messed, I believe with the classes/styles clashing with each others.
Any Idea what will work good as an replacement ??
Thanks for your help.
I was also looking for a similar plugin and then I found mdl-jquery-modal-dialog
https://github.com/oRRs/mdl-jquery-modal-dialog
I used this because the other one I found was having issue on IE11. This one works fine.
<button id="show-info" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent">
Show Info
</button>
Here a JSFiddle: https://jsfiddle.net/w5cpw7yf/
I came up with a pure JavaScript Solution for this
You can use the default bootstrap data attributes for the buttons, and make sure that your buttons and modals have their own unique IDs.
You need to have Material Design Lite's JS included before using this JavaScript
Check out the code. Any reviews are welcomed. :)
// Selecting all Buttons with data-toggle="modal", i.e. the modal triggers on the page
var modalTriggers = document.querySelectorAll('[data-toggle="modal"]');
// Getting the target modal of every button and applying listeners
for (var i = modalTriggers.length - 1; i >= 0; i--) {
var t = modalTriggers[i].getAttribute('data-target');
var id = '#' + modalTriggers[i].getAttribute('id');
modalProcess(t, id);
}
/**
* It applies the listeners to modal and modal triggers
* #param {string} selector [The Dialog ID]
* #param {string} button [The Dialog triggering Button ID]
*/
function modalProcess(selector, button) {
var dialog = document.querySelector(selector);
var showDialogButton = document.querySelector(button);
if (dialog) {
if (!dialog.showModal) {
dialogPolyfill.registerDialog(dialog);
}
showDialogButton.addEventListener('click', function() {
dialog.showModal();
});
dialog.querySelector('.close').addEventListener('click', function() {
dialog.close();
});
}
}
<!-- Button to trigger Modal-->
<button class="mdl-button mdl-js-button" id="show-dialog" data-toggle="modal" data-target="#upload-pic">
Show Modal
</button>
<!-- Modal -->
<dialog id="upload-pic" class="mdl-dialog mdl-typography--text-center">
<span class="close">×</span>
<h4 class="mdl-dialog__title">Hello World</h4>
<div class="mdl-dialog__content">
<p>This is some content</p>
</div>
</dialog>
I use MDL with bootstrap and the modal is displayed correctly after adding the data-backdrop attribute this to the modal element:
<dialog data-backdrop="false">
Hope it helps!