Ionic framework infinite scroll just have called one time - angularjs

I have tried to build mobile app with ionic framework in the recent day. And I found that the list directy of ionic is very slow when it has just about 30 items or more, it's very bad. I've tried the bindonce method in angularjs to reduce the number of watchers but it's not help much. So I tried to use the infinite scroll function that ionic has.
My template is like this:
<view title="'Pet Information'">
<content has-header="true" has-tabs="true" on-infinite-scroll="loadMore">
<list>
<item ng-repeat="pet in pets" type="item-text-wrap" href="#/tab/pet/{{pet.id}}">
<h3>{{pet.title}}</h3>
<p>{{pet.description}}</p>
</item>
</item>
</list>
</content>
</view>
And my controller
.controller('PetIndexCtrl', function($scope, PetService) {
$scope.pets_all = PetService.all();
$scope.pets = [];
// Add just 10 pets at the first time
var count = 0;
for (var i = 0; i < 10; i++) {
$scope.pets.push($scope.pets_all[i]);
count++;
};
$scope.loadMore = function() {
var curr_count = count;
for (var i = curr_count; i < curr_count + 10; i++) {
if (i < $scope.pets_all.length) {
$scope.pets.push($scope.pets_all[i]);
count++;
} else {
return;
}
}
}
})
My idea is that the list will just load 10 items at the first time, and everytime user scroll to bottom edge of the phone it will call the loadMore function that will load 10 more items. But it seem that the loadMore function just have called one time, so that all I have is just the list of 20 items while my array is more than 100 items.
Maybe I'm wrong or the infinite scroll of ionic framework have error?

Seems there is a new way to do this in Ionic 2. Now you have to broadcast an event in the loadMore callback, to signalize that loading is finished.
$scope.$broadcast('scroll.infiniteScrollComplete');

I found what wrong with my code. I just missed the done callback in the code. I saw it in the example on github but I've just thought that it's a option not require, poor me :)
$scope.loadMore = function(done) {
$timeout(function(){
var curr_count = count;
for (var i = curr_count; i < curr_count + 7; i++) {
if (i < $scope.pets_all.length) {
$scope.pets.push($scope.pets_all[i]);
count++;
} else {
return;
}
}
done();
}, 1000);
}

For angular2/typescript ionic firebase app, the code below worked.
Set $event.state = "closed"; to call the function more than one time. The complete code is given below for reference.
In the component,
Import
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
Declare
limit: number = 10;
itemRef:FirebaseListObservable<any[]>;
itemList:any;
loadeditemList:any;
Firebase Call
getData(){
this.itemRef = this.firebase.list('quote', {
query: {
orderByChild: 'title',
limitToFirst:this.limit
}
});
}
Scroll Call
onInfiniteScroll($event:any) {
this.limit += 10;
this.getData();
this.itemRef.forEach((itemList:any) => {
let items:any = [];
itemList.forEach( (item:any) => {
items.push(item);
return false;
});
this.itemList = items;
this.loadeditemList = items;
$event.state = "closed";
});
}
HTML
<ion-list>
...
</ion-list>
<ion-infinite-scroll (ionInfinite)="onInfiniteScroll($event)">
<ion-infinite-scroll-content></ion-infinite-scroll-content>
</ion-infinite-scroll>
Call getData() and inject firebase in constructor.
private firebase: AngularFireDatabase

Related

Clusterize scroll errors in angular js Typescript

Trying to implement the lazy loading using the Clusterize js in Angular js Typescript. Unfortunately getting the errors.
Any expert advice please?
HTML VIEW
<div id="scrollArea" class="clusterize-scroll">
<ul id="contentArea" class="clusterize-content"></ul>
</div>
Angular JS
namespace Cis2.VC.OrderCreate {
angular.module("cis2")
.directive("cis2VCOrderCreate", directiveDefinition);
templateUrl = "sections/vc/columns/vcOrderCreate/view.html";
function directiveDefinition () {
directive = {
"bindToController": true,
"controller": cis2VCOrderCreateController,
"templateUrl": templateUrl
};
}
class cis2VCOrderCreateController implements Cis2.Finder.Column.IEntityCreator {
constructor() {
activate () {
let rows = [];
for(var i = 1; i < 50000; i++) {
rows.push(i);
}
console.log(rows);
var clusterize = new Clusterize({
rows: rows,
scrollId: 'scrollArea',
contentId: 'contentArea'
});
}
}
}
Console errors
TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
at Clusterize.html (http://localhost:63342/cis-ui-src/public/lib/clusterize/clusterize.js:341:26)
You are supposed to supply markup to the rows option. Numbers won't work. From the documentation:
rows
If you render rows by yourself - pass array of tags in String. This way is preferable.
If you need to use existing markup - do not specify this option at all.
activate () {
let rows = [];
for(var i = 1; i < 50000; i++) {
rows.push("<li>" + i + "</li>"); //this must be a string of markup
}
console.log(rows);
var clusterize = new Clusterize({
rows: rows,
scrollId: 'scrollArea',
contentId: 'contentArea'
});

Line Chart not updating everytime

So I am using Line from react-chartjs-2 and I've added an onClick event to the labels which blur all the lines except the current one.
Here is my onClick function code:
legend:{
'onClick': function(e, legendItem) {
var index = legendItem.datasetIndex;
var ci = this.chart;
console.log(ci.data.datasets);
//var alreadyHidden = (ci.data.datasets[index].borderColor === ci.data.datasets[index].accentFadedColor) ? false : true;
for(var i=0; i< ci.data.datasets.length;i++){
if (i !== index) {
ci.data.datasets[i].borderColor = ci.data.datasets[i].accentFadedColor;
} else if (i == index){
ci.data.datasets[i].borderColor = ci.data.datasets[i].accentColor;
}
}
that.updateValue(index);
ci.update();
}
The problem is, the function updates the chart on first click but not after that. Though I can see the updates values with console.log()
Any idea what I am doing wrong?
Update:
So apparently my chart is working fine. Inside the onClick() I'm making call to another function ( which sets state ) which is causing this behavior.
Here's a link to stackblitz
Any advice?
So apparently, just making the function call inside onClick() but before updating the datasets solved the problem.
I think rendering after setState and canvas update were somehow mixing up.
Here's the updated code:
legend:{
'onClick': function(e, legendItem) {
var index = legendItem.datasetIndex;
var ci = this.chart;
that.updateValue(index);
for(var i=0; i< ci.data.datasets.length;i++){
if (i !== index) {
ci.data.datasets[i].borderColor = ci.data.datasets[i].accentFadedColor;
ci.data.datasets[i].lineWidth = ci.data.datasets[i].highlightedWidth;
} else if (i == index){
ci.data.datasets[i].borderColor = ci.data.datasets[i].accentColor;
ci.data.datasets[i].lineWidth = ci.data.datasets[i].fadedWidth;
}
}
ci.update(); }

Intermittent "element is not found" in protractor

In Protractor, I am trying to write a function to simulate clicking a responsive navigation menu item (i.e. menu is either a bar of dropdowns across the top or a hamburger link if mobile). The structure of the bar is defined by MEANJS with a few id="" attributes mixed in.
It seems to work exactly as I want when I run it one spec at a time, like so:
protractor app/tests/e2e/conf.js --suite mySuite
but when I run with the full test (that only has 4 tests in it), like so:
protractor app/tests/e2e/conf.js
I start to get this error intermittently (source of error is in 2 places below):
Failed: element not visible
Here's my function
commonPOs.clickNavBar = function(mainTab, linkUrl) {
var deferred = protractor.promise.defer();
var hamburger = element(by.id('nav-hamburger'));
var linkCssExpression = 'a[href*="' + linkUrl + '"]';
hamburger.isDisplayed().then(function(result) {
if ( result ) {
hamburger.click().then(function() {
var navBar = hamburger
.element(by.xpath('../..'))
.element(by.id('myapp-navbar'));
return clickItNow(mainTab, linkUrl, navBar);
});
} else {
return clickItNow(mainTab, linkUrl, element(by.id('myapp-navbar')));
}
});
return deferred.promise;
function clickItNow(mainTab, linkUrl, navBar) {
var link;
if(mainTab) {
// if mainTab was passed, need to
// click the parent first to expose the link
var parentLink;
if (mainTab == 'ACCTADMIN') {
parentLink = navBar.element(by.id('account-admin-menu'));
}
else {
parentLink = navBar.element(by.linkText(mainTab));
}
expect(parentLink.isPresent()).toBeTruthy();
parentLink.click(); // FIRST PLACE ERROR HAPPENS
link = parentLink.element(by.xpath('..')).element(by.css(linkCssExpression));
}
else {
link = navBar.element(by.css(linkCssExpression));
}
expect(link.isPresent()).toBeTruthy();
link.click(); // SECOND PLACE ERROR HAPPENS
return deferred.fulfill();
}
};
I have tried to use both of these but neither work:
browser.sleep(500);
browser.driver.wait(protractor.until.elementIsVisible(parentLink));
What NOOB async error am I making?
Self answer... but any better answer will win the green check mark!!!
EDITED - After more problems, found a nice 'waitReady()' contribution from elgalu.
There were a few obvious problems with my original code, but fixing them did not solve the problem. After working through what I could, the solution seemed to be the expect(navBar.waitReady()).toBeTruthy(); lines I added. I also had to split the nested function out. Anyway, here is the new code that seems to be working now. A better (comprehensive) answer will get the green checkmark! I'm pretty sure there's a flaw or 2 here.
commonPOs.clickNavBar = function(mainTab, linkUrl) {
var deferred = protractor.promise.defer();
var hamburger = element(by.id('nav-hamburger'));
hamburger.isDisplayed().then(function(result) {
if ( result ) {
hamburger.click().then(function() {
var navBar = hamburger
.element(by.xpath('../..'))
.element(by.id('myapp-navbar'));
return clickItNow(mainTab, linkUrl, navBar, deferred);
});
} else {
return clickItNow(mainTab, linkUrl, element(by.id('myapp-navbar')), deferred);
}
});
return deferred.promise;
};
function clickItNow(mainTab, linkUrl, navBar, deferred) {
var targetLink;
var linkCssExpression = 'a[href*="' + linkUrl + '"]';
expect(navBar.waitReady()).toBeTruthy();
if(mainTab) {
// if mainTab was passed, neet to
// click the parent first to expose the link
var parentTabLink;
if (mainTab == 'ACCTADMIN') {
parentTabLink = navBar.element(by.id('account-admin-menu'));
}
else {
parentTabLink = navBar.element(by.id('main-menu-' + mainTab));
}
// expect(parentTabLink.isDisplayed()).toBeTruthy();
expect(parentTabLink.waitReady()).toBeTruthy();
parentTabLink.click();
targetLink = parentTabLink.element(by.xpath('..')).element(by.css(linkCssExpression));
}
else {
targetLink = navBar.element(by.css(linkCssExpression));
}
expect(targetLink.isDisplayed()).toBeTruthy();
targetLink.click().then(function() {
return deferred.fulfill();
});
}

How to fill "ion-checkbox ng-repeat" at the time of page load

What I need to do?
I am trying to load the "ion-checkbox ng-repeat" "onDeviceReady" automatically at the time of page load. Below is the HTML code.
<ion-checkbox ng-repeat="item in devList"
ng-model="item.checked"
ng-checked="item.checked">
{{ item.text }}
</ion-checkbox>
But the "ion-checkbox ng-repeat" is getting loaded only when click event is triggered.
The below is the angular-js code which needs to be triggered automaticlly at the time of page load.
Problem: The data for "ion-checkbox ng-repeat" is not getting filled at the time of page load.
Can anyone help to solve the issue.
angular.module('app', ['ionic'])
.controller('AppCtrl', function($scope) {
$scope.devList = [];
window.addEventListener("deviceready", onDeviceReady, true);
function onDeviceReady() {
var options = new ContactFindOptions();
options.filter = ""; // empty search string returns all contacts
options.multiple = true; // return multiple results
filter = ["*"]; // return contact.displayName field
//document.getElementById("lan").innerHTML = lan;
// find contacts
navigator.contacts.find(filter, onSuccess, onError, options);
}
function onSuccess(contacts) {
for (var i = 0; i < contacts.length; i++) {
$scope.devList[i] = {text:""+contacts[i].name.formatted, emails:{email:""+contacts[i].emails[0].value,checked:false}, phno:{phone:""+contacts[i].phoneNumbers[0].value,checked:false},addres:{address: contacts[i].addresses||[],checked:false},checked: false};
$scope.emails[i] = {email:""+contacts[i].emails[0].value+""};
}
}
function onError(contactError) {
alert('onError!');
}
}
You need to call $apply if you do anything outside of angular context
function onSuccess(contacts) {
for (var i = 0; i < contacts.length; i++) {
$scope.devList[i] = {text:""+contacts[i].name.formatted, emails:{email:""+contacts[i].emails[0].value,checked:false}, phno:{phone:""+contacts[i].phoneNumbers[0].value,checked:false},addres:{address: contacts[i].addresses||[],checked:false},checked: false};
$scope.emails[i] = {email:""+contacts[i].emails[0].value+""};
$scope.$apply();
}
}

Why is that the UI is not refreshed when data is updated in typescript and angularjs program

I learn typescript and angularjs for a few days,and now I have a question that confuses me for days, I want to make a gps tracking system, so I try to write a service like this:
1.
module Services {
export class MyService {
getGpsPeople(): Array<AppCommon.GPSPerson> {
var gpsPeople = new Array<AppCommon.GPSPerson>()
for (var i = 0; i < 10; i++) {
var tempPerson = new AppCommon.GPSPerson({ name: "username" + i.toString() });
gpsPeople.push(tempPerson);
}
return gpsPeople;
}
} // MyService class
}
A controller like this:
module AppCommon {
export class Controller {
scope: ng.IScope;
constructor($scope: ng.IScope) {
this.scope = $scope;
}
}
}
module Controllers {
export interface IMyScope extends ng.IScope {
gpsPeople: Array<AppCommon.GPSPerson>;
}
export class MyController extends AppCommon.Controller {
scope: IMyScope;
static $inject = ['$scope','myService'];
constructor($scope: IMyScope,service:Services.MyService) {
super($scope);
$scope.gpsPeople = service.getGpsPeople();
}
}
}
3.The GPSPerson class like this:
export class GPSPoint {
latitude = 0;
longtitude = 0;
constructor(la: number, lg: number) {
this.latitude = la;
this.longtitude = lg;
}
}
export interface IPerson {
name: string;
}
export class GPSPerson
{
name: string;
lastLocation: GPSPoint;
countFlag = 1;
historyLocations: Array<GPSPoint>;
timerToken: number;
startTracking() {
this.timerToken = setInterval(
() => {
var newGpsPoint = null;
var offside = Math.random();
if (this.countFlag % 2 == 0) {
newGpsPoint = new GPSPoint(this.lastLocation.latitude - offside, this.lastLocation.longtitude - offside);
}
else {
newGpsPoint = new GPSPoint(this.lastLocation.latitude + offside, this.lastLocation.longtitude + offside);
}
this.lastLocation = newGpsPoint;
this.historyLocations.push(newGpsPoint);
console.log(this.countFlag.toString() + "+++++++++++++++++++" + this.lastLocation.latitude.toString() + "----" + this.lastLocation.longtitude.toString());
this.countFlag++;
}
, 10000);
}
stopTracking() {
clearTimeout(this.timerToken);
}
constructor(data: IPerson) {
this.name = data.name;
this.lastLocation = new GPSPoint(123.2, 118.49);
this.historyLocations = new Array<GPSPoint>();
}
}
The problem is:
1.Should I make the GPSPerson class a Controller?
2.The setinterval works but the UI dose not change(when I hit button ,it changes,the button do nothing )?
I'm a beginner of ts and angular,and have no experience with js, I do not know if I have explained it clearly, hope someone can help me, thanks!
setInterval works but the ui dose not change
This is because the angular $digest loop does not run on completion of setInterval. You should use $interval service https://docs.angularjs.org/api/ng/service/$interval as that tells Angular to do its dirty checking again.
You just need to provide MyService access to $interval though. Inject it using $inject (see https://www.youtube.com/watch?v=Yis8m3BdnEM&hd=1).
1.should i make the GPSPerson class a Controller?
No. Its an array of JavaScript objects inside the controller and that is fine.
I would separate the tracking logic and keep just the data in GPSPerson. For the tracking logic I would make a factory.
My examples are not in Typescript but I'm sure you will have no problem in converting the code if you want.
This is a link to a Plunk
I've made a much simpler example but I think you'll understand the idea.
The factory would have two methods for start and stop tracking. They will take a person as parameter.
app.factory('tracking',function($interval){
var trackingInterval;
var trackingFn = function(person){
var currentPos = Math.floor(Math.random()*10);
var newPosition = {id:person.positions.length, position:currentPos};
person.positions.push(newPosition);
};
var startTracking = function(person){
person.interval = $interval(function(){
trackingFn(person);
},2000);
};
var stopTracking = function(person){
console.log('STOP');
$interval.cancel(person.interval);
};
var getNewTrack = function(){
};
return {
startTracking: startTracking,
stopTracking: stopTracking,
};
});
I've also made a very simple directive to show the data
app.directive('position',function(){
return {
templateUrl: 'positionTemplate.html',
link: function(scope, element,attrs){
}
}
});
and the template look like this
<div>
<button ng-click="startTracking(person)">Start tracking</button>
<button ng-click="stopTracking(person)">Stop tracking</button>
<p>{{person.name}}</p>
<ul>
<li ng-repeat="pos in person.positions">{{pos.position}}</li>
</ul>
</div>
And the directive would be called this way
<div ng-repeat="person in people">
<div position></div>
</div>
I'm not saying that this is a better solution but that is how I would do it. Remember it is just a model and needs a lot of improvement.

Resources