I'm working on a chatapp and like most chatapps my app shows a list of messages.
The list is generated with ng-repeat.
I have reversed the messages so that the newest are at the bottom and
oldest at the top.
Currently when my app loads the list is scrolled down with
$ionicScrollDelegate
I don't like it doing it this way. It's not the correct way and sometimes this gives me some performance and loading issues when opening my app.
I was wondering if there is another, forced way, to start/render the list from the bottom to the top without the need for scrolling it to the bottom like I do now.
This is the code I currently use:
In my HTML:
<script type="text/ng-template" id="home.html">
<ion-view ng-controller="HomeController" title="Home">
<ion-content>
<ion-list>
<ion-item ng-repeat="message in messages track by $index">
{{message}}
</ion-item>
</ion-list>
</ion-content>
</ion-view>
</script>
In my app.js:
app.run(function($ionicPlatform, $state, $timeout, $localStorage, $location, $rootScope, $ionicScrollDelegate) {
document.addEventListener('deviceReady', function() {
setTimeout(function() {
$ionicScrollDelegate.scrollBottom(true);
}, 500);
});
})
Add a watch instead:
$scope.$watch('messages', function(newValue, oldValue) {
$ionicScrollDelegate.scrollBottom(true);
}, true);
The reason you are getting this behaviour is cause deviceReady does not guarantee that the items are rendered yet, which causes your list to not scroll anywhere at all. With a watch, you'll scroll when the array has been loaded and the values are already there.
Related
I have a ion menu which contain 2 links , Dashboard, favorite.
At the very first time Dashboard page is loaded for application with content. Now when i switch to another menu item. My Content seems populated first after that side menu hides. This causes user experience using the app is bad.
Can we hide side menu first then populated content.
I also tried following steps
<ion-list>
<ion-item menu-close href="#/app/articles" ng-click="toggleLeft()">
Dashboard
</ion-item>
<ion-item menu-close href="#/app/favourite" ng-click="toggleLeft()">
Favorite
</ion-item>
</ion-list>
and
$scope.toggleLeft = function() {
$ionicSideMenuDelegate.toggleLeft();
};
which also doesn't make any effect .
Here is my Dashboard page:-
<ion-view>
<ion-content>
<ion-list can-swipe="true">
<ion-item class="list article-list" ng-repeat="article in articles">
my divs
</ion-item>
</ion-list>
</ion-content>
</ion-view>
and controller is :
.controller('ArticleCtrl', function($scope, $rootScope ,articleService) {
//This is fetching data from database (**Sql lite**)
articleService.fetchArticles().then(function(articles){
$rootScope.articles = articles;
}
});
Similar code for Favorite with different controller.
Any suggestions?
Thanks
This is actually default behaviour but it can be changed. Ionic offers a few events that can be watched and you can than control when to do something. In your case I would populate the $scope only after the view fully enters the screen.
$scope.$on('$ionicView.enter', function (viewInfo, state) {
$scope.data = _getData();
});
So basically the data that you are filling the view with needs to be associated with the $scope only when the view is fully visible.
Edit:
.controller('ArticleCtrl', function($scope, $rootScope ,articleService) {
//This is fetching data from database (**Sql lite**)
var _articles;
articleService.fetchArticles().then(function(articles){
_articles = articles;
}
$scope.$on('$ionicView.enter', function (viewInfo, state) {
$scope.articles = _articles;
});
});
I am setting the scope with data but all the time getting empty screen.
tried to use some $scope.$apply but it isn't helping.
I can see the scope.items isn't empty...
I can see values only one I click on other tabs.
my code:
.controller('AccountCtrl', function ($scope, $timeout) {
if (localStorage.getItem("itemHistory") !== null) {
$scope.items = localStorage["itemHistory"].split(',');
$scope.$safeApply($scope);
}
});
tab that navigate to template
<!-- Dashboard Tab -->
<ion-tab title="History" icon-off="ion-ios-pulse" icon-on="ion-ios-pulse-strong" href="/tab/account">
<ion-nav-view name="tab-account"></ion-nav-view>
</ion-tab>
my temp:
<ion-nav-bar>
<ion-nav-back-button>
</ion-nav-back-button>
</ion-nav-bar>
<ion-view view-title="Recent Searches">
<ion-content class="padding">
<ul>
<li ng-repeat="item in items">
{{ item }}</li>
</ul>
</ion-content>
</ion-view>
thanks for helping!
I dont see anywhere in your view where the items is being referred. Can you please provide a jsfiddle where I can see the problem? otherwise just looking at this code, I cant make out anything.
And you should not be using explicit apply in this case. apply is a very dangerous call which has to be used with lot of due diligence. It would trigger a digest cycle that trickles up till the rootscope which is what you may NOT want.
I find the issue, it was simple mistake on app.js didnt set the correct view on the app.js
I'm using onsen ui and angular for the first time.
Basically I'm looking to navigate from the index to another page with a full screen carousel, with a specific carousel-item showing.
I started out with this code which is more or less the onsen ui sample carousel code modified to use ng-repeat and to set an initial index.
html
<ons-navigator title="Navigator" var="myNavigator">
<ons-page ng-controller="MyController">
<ons-toolbar>
<div class="center">Carousel</div>
</ons-toolbar>
<ons-carousel swipeable overscrollable auto-scroll fullscreen var="carousel">
<ons-carousel-item ng-repeat="i in ['gray', '#085078', '#373B44', '#D38312']" style="background-color: {{i}};">
<div class="item-label">{{i}}</div>
</ons-carousel-item>
<ons-carousel-cover>
<div class="cover-label">Swipe left or right</div>
</ons-carousel-cover>
</ons-carousel>
</ons-page>
</ons-navigator>
app.js
var app = ons.bootstrap();
app.controller("MyController", function($scope) {
ons.ready(function() {
carousel.setActiveCarouselItemIndex(2);
});
});
And that seemed to work fine when the carousel was the main page.
setting initial-index didn't seem to work with ng-repeat.
html
<ons-carousel swipeable overscrollable auto-scroll fullscreen initial-index="2" var="carousel">
Then, when I tried navigating to the page with the carousel, it said that carousel was undefined.
I also tried adding an event listener on the carousel, since it didn't seem to be loaded at that point.
document.addEventListener('ons-carousel:init', function() {
carousel.setActiveCarouselItemIndex(2);
});
The event listener triggered, but the setActiveCarouselItemIndex didn't seem to do anything.
This same code works when I do not use ng-repeat and have a bunch of ons-carousel-items in the html.
What I started out with: http://codepen.io/anon/pen/aObNqW
A simple example of where I am stuck: http://codepen.io/anon/pen/yNLOvY
If there is a better way to do this, I'd love to know!
Your code is fine, the problem is that you are trying to switch the carousel page when onsen is ready, which is before the carousel element is created. You can add a timeout function to make it work, or just call the script after the carousel has been loaded. If you choose the first option, just modify you controller in this way:
var app = ons.bootstrap();
app.controller("MyController", function($scope) {
ons.ready(function() {
setImmediate(function(){
carousel.setActiveCarouselItemIndex(2);
});
});
});
HERE you can find a working CodePen example
I have an ion-slide-box using ng-repeat showing images that are fetched from a remote server.
<ion-slide-box on-slide-changed="slideChanged(index)" auto-play="true" does-continue="true">
<ion-slide ng-repeat="slide in files">
<img ng-src="{{slide.filename}}" >
</ion-slide>
</ion-slide-box>
and the js:
$scope.getFiles = function() {
Files.query({
//some parameters
}).$promise.then(function(data) {
$scope.files = data;
});
When getFiles() is called (which fires a service that is not shown here), $scope.files is updated successfully. But the DOM (the images loaded into the ion-slide-box) are not automatically updated. How do I refresh the DOM?? I have tried timeout (nothing happens) and $scope.$apply (throws error that $dispatch already sterted.)
Even though this is a mobile app, when testing in desktop browser, I can re-size the browser and the images automatically display. So either I figure out how to keep this updated, or find a better image slider/carousel to use.
You need to call the $ionicSlideBoxDelegate.update() method after you have changed the scope variable. The slidebox does not automatically listen for changes in the latest version of the framework.
http://ionicframework.com/docs/nightly/api/service/$ionicSlideBoxDelegate/
You should set a delegate-handle attribute and use that to make sure you don't interfere with any other slideboxes you make have loaded at the time.
html
<ion-slide-box delegate-handle="image-viewer" on-slide-changed="slideChanged(index)" auto-play="true" does-continue="true">
<ion-slide ng-repeat="slide in files">
<img ng-src="{{slide.filename}}" >
</ion-slide>
</ion-slide-box>
js
$scope.getFiles = function() {
Files.query({
//some parameters
}).$promise.then(function(data) {
$scope.files = data;
$ionicSlideBoxDelegate.$getByHandle('image-viewer').update();
});
}
I believe this will change in the near future so your code will work as expected. They are working on an updated slidebox component but it was pulled at the last minute to speed up the latest release.
You need to wait the ng-repeat is rendered before being able to update() your slidebox, use this directive :
angular.module('starter')
.directive('repeatDone', function () {
return function (scope, element, attrs) {
if (scope.$last) { // all are rendered
scope.$eval(attrs.repeatDone);
}
}
})
HTML :
<ion-slide-box>
<ion-slide ng-repeat="day in week" repeat-done="repeatDone()" >
In your controller
$scope.getFiles = function() {
Files.query({
//some parameters
}).$promise.then(function(data) {
$scope.week = data;
});
$scope.repeatDone = function() {
$ionicSlideBoxDelegate.update();
//$ionicSlideBoxDelegate.slide($scope.week.length - 1, 1);
};
To work properly must do the following .
After 8 hours of testing and achieve internet search to find the right solution.
<ion-slide-box on-slide-changed="slideChanged(index)" auto-play="true" does-continue="true">
<ion-slide ng-repeat="slide in files">
<img ng-src="{{slide.filename}}" >
</ion-slide>
</ion-slide-box>
delegate-handle="image-viewer" does not have to be in ion-slide-box must be in <ion-content delegate-handle="mainhanddle">
and from the controller must execute these two commands:
$ionicSlideBoxDelegate.update();
$ionicSlideBoxDelegate.$getByHandle('mainhanddle').update();
At the end of each call.
To add this here, <ion-slide-box delegate-handle="image-viewer" other important properties such as cancel and for example nr-repeat does not work properly.
Well, I hope I have helped , if you need more details please do not hesitate to contact me.
Instead of:
$scope.files = data;
Try:
$scope.files.splice(0,$scope.files.length);
$scope.files = $scope.files.concat(data);
That should allow you to update the content of the array without losing the initial memory reference.
Note: I am assuming that $scope.files and data are arrays.
I achieved to resolve this trouble using the library ion-image-lazy-load. Try out with this approach:
JS:
.controller('myCtrl', function ($scope) {
$scope.images = imagesArray(); // Get array of objects with image urls
$scope.slider = {
options: {
direction: 'horizontal',
slidesPerView: '1',
grabCursor: true
}
};
});
View:
<ion-content lazy-scroll>
<ion-slide-box style="width:100% !important">
<ion-slide ng-repeat="slide in images">
<img image-lazy-src="{{slide.url}}" lazy-scroll-resize="true" image-lazy-loader="lines" style="width:100%">
</ion-slide>
</ion-slide-box>
</ion-content>
So far, what I have is straight off the Angular UI example:
Controller:
var ModalDemoCtrl = function ($scope, $modal) {
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl
});
};
};
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.close = function () {
$modalInstance.dismiss('cancel');
};
};
And this section, which is just in sitting in the .html for the whole page this modal is on.
Html:
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
<button class="btn btn-warning" ng-click="close()">X</button>
</div>
<div class="modal-body">
Stuff
</div>
</script>
This works just fine. But I'm trying to refactor some things out and organize my code, so I would like to move the modal html to its own file. When I do so, and try to use it as by changing the templateUrl to the path: \tmpl\myModalContent.html, it doesn't show up. The backdrop still appears and inspecting the page shows that it loaded correctly, but just won't show up.
I've tried changing the css for the modal per these suggestions with no difference.
My question is, why does this work fine if the script tag is in the main html, but not at all if it is in it's own file?
Here is a plnkr that shows what I mean. If you copy what is in the template.html and place it right above the button in the index.html file, it works...
Remove template declaration for template.html and just put raw HTML in there like this:
<!--script type="text/ng-template" id="template.html"-->
<div class="modal-body">
Hello
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="cancel()">OK</button>
</div>
<!--/script-->
Your plnkr worked fine with second click to the button. It'd show the modal as expected. The reason it showed up with second click is because Angular would load up the 'uncompiled' template the first time, then it compiled the template to raw HTML which is ready for your subsequent clicks.
EDIT: Also, when you put the template code right in index.html, Angular compiles the template during its initial pass through the DOM; that's why the modal seemed to work.
Well I am clearly a dummy. All I had to do was include my new file in the main view.
<div ng-include="'path-to-file.html'"></div>
Then calling it from the controller was easy, all I needed was the id (modalContent.html) as the templateUrl.
Just keep swimming, and you'll eventually get there :)