Angular view not updating after $http call - angularjs

I'm trying to get the list of stacks from my server and populate view with ng-repeat but for some reason the view doesn't update even though the scope is updated. Here's my code
$scope.my = {stacks : []};
$http.post('https://myurl',{"action":"login"}).success(function (data, status, headers, config) {
$scope.my.stacks = data.stacks;
console.log($scope.my.stacks); //logs the correct value
//$scope.$apply() // don't need to do it, tried but it raised the already running digest cycle error
}).error(function (data, status){
alert(data)
});
And here's my HTML
//{{stacks}} if I do this, the view updates OK, strange
<ion-slide-box on-slide-changed="slideHasChanged($index)">
<ion-slide ng-repeat="stack in my.stacks">
<div class="list card">
<div class="item item-avatar">
<img src="./img/ionic.png">
<h2>{{stack.stack_name}}</h2>
<p>{{stack.stack_description}}</p>
</div>
<div class="item item-image">
<img style="max-width:100%;height:auto" src="./img/Test_Icon.png">
</div>
<a class="item item-icon-left assertive" ng-click="chooseStack(stack)">
<i class="icon ion-social-buffer-outline"></i>
Go to Stack
</a>
</div>
</ion-slide>
</ion-slide-box>
Also, the view updates as soon as I change the orientation. What am I doing wrong?

The problem is the Ionic slidebox. You need to let it know that the scope variable has been updated as described here.

Related

How to add item to array in ng-repeat with lazy loading

I have an array of comments. For each of the comment, I have a hidden form where you can reply. That item in each object of the array is called: comment.showSubCommentForm. When I click on Reply, I want to be able to reply with a comment and the function called: commentOnSubPost.
I load my data in chunks of 10 (lazy loading) which I am doing by using the infinite-scroll-distance Angular module. Every time I reach the end of the page, I get the next 10 items and add them to the $scope.comments array, which you can see below is what I am running my ng-repeat on .
The problem is that, although the commentOnSubPost works really well as in it adds the reply to the comment, I have reload the data so I can get the updated $scope.comments with the new reply. BUT - as I have implemented lazy loading, how do I do that if say I am on the 20th page?
I looked at some similar posts but their approach from mine was very different so am very lost.
My HTML:
<div infinite-scroll='loadMore()' infinite-scroll-distance='1'>
<div class="row">
<div ng-repeat="comment in comments track by $index">
<ul class="comments">
<li class="clearfix">
<a ng-href="#">SomeLink</a>
<div>
<p >{{ comment.date }} <a ng-href="/wall#/profile/main/{{ comment.userID }}">{{ comment.fullname }}</a> says <i class="commentReply"><a href ng-click="comment.showSubCommentForm = !comment.showSubCommentForm"><small>Reply</small></a></i></p>
<p class="mainComment">
{{ comment.comment }}
<p class="meta"><i class="showReply"><a href ng-click="comment.showReplies = !comment.showReplies"><small>Show Replies</small></a></i></p>
</p>
</div>
<div ng-show="comment.showSubCommentForm">
<form ng-submit="commentOnSubPost( post._id, comment._id, subComment)">
<div class="form-group">
<div class="form-group">
<textarea ng-model="subComment" class="form-control" placeholder="Reply to the above comment"></textarea>
</div>
<input type="submit" value="Reply">
</div>
</form>
<div ng-show="comment.showReplies" ng-repeat="subComment in comment.subComments track by $index">
<ul class="comments">
<li class="clearfix">
<a ng-href="/wall#/profile/main/{{ comment.userID }}"><img ng-src="{{ subComment.profilePic }}" class="avatar" style="border-radius: 50%"></a>
<div class="post-subComments">
<p class="meta">{{ subComment.date }}<a ng-href="SomeLink"> {{ subComment.fullname }}</a></p>
<p>
{{ subComment.comment }}
</p>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
I won't add too much information here as I don't want to overload this post but if you need any particular information, please do let me know. I just need to know how do I reload the data (or not if there's another approach) so that I can get the latest data.
I hope this makes sense.
Thanks in advance!
Like you have $scope.comments = []; then on each call you just have to append upcoming data to this array.
Like $scope.comments = $scope.comments.concat(response.data);
In this way lazy loading will work.
I fixed this with making a Node route which gives back the exact comment I have just added. Then, I found that commentID in my $scope.comments and made it's subCommenemts to the one I have locally.
It does the job and the data is real. One drawback is that if at that time, there's another comment, it won't show up until the page's refreshed but I'm not too fussed about that.

AngularJS select item on array

I´m stuck in something that seems to be quite easy, but I can´t find the way to go.
I´m developing an hybrid mobile app in AngularJS with Intel XDK, and I have to show a array of results from JSON, and allow users to click on one of them to get further info. I need to show the list on page A, and the complete info on page B.
By now I´ve done all but showing the full info on page B, even if I managed to get the ID of the item clicked. What I have is
Controller.js
.controller('myController', function($scope,$http) {
//This to get de JSON
$http({ method: 'GET',
url: '/lib/myData.json'
}).then(function successCallback(response) {
$scope.misDatos = response.data;
}, function errorCallback(response) {
console.error ("no json data");
});
//This to get the item clicked
$scope.setMaster = function(section) {
$scope.selected = section;
console.log("Item Clicked: " + $scope.selected.id);
};
})
PageA.html
<div class="card" ng-repeat="dato in misDatos" ng-click="setMaster(dato)" >
<div class="item item-divider">
{{dato.objetivo}}
</div>
<div class="item item-text-wrap">
<p>{{dato.descripcion}}...</p>
</div>
<div class="item item-divider">
</i> Más información
</div>
</div>
PageB.html
<div class="list card" ng-repeat="dato in misDatos | filter: {id:selected}">
<div class="item item-avatar">
<h4>{{dato.objetivo}}</h4>
</div>
<div class="item">
<p>{{dato.descripcion}}</p>
</div>
</div>
In my console I can see the correct ID clicked on page A
Item Clicked:3
But i can´t make my "B" page to show the data from the result selected...
Do you know what am I doing wrong? Is there a nice tutorial to do so?
Thanks everyone in advance!
In page B, you should be binding to the value of the selected item as below
<div class="list card">
<div class="item item-avatar">
<h4>{{selected.objetivo}}</h4>
</div>
<div class="item">
<p>{{selected.descripcion}}</p>
</div>
</div>
This is because the value of the selected item is now bound to the scope through $scope.selected
Finally I did it. This is how it works for me; wish it could help someone, bcos I spent the whole with this issue.
The problem, as Evans said, was that there was no data available on "page B", so the solution is to send the info to that page. I´ve used the reference of http://excellencenodejsblog.com/angularjs-sharing-data-between-controller/, placing the factory on the "app.js" file, and the controllers on the "controllers.js" file.
Cheers!

In what scope is ng-repeat "as alias" available

I'm using angular 1.4.8. I want to save filtered result from ng-repeat and use it to determine whether to enable a "load more" button at the bottom of the list. I have looked at this question:
AngularJS - how to get an ngRepeat filtered result reference
And many others, they suggest to use "as alias" from ng-repeat, here's what my code looks like:
<ul class="media-list tab-pane fade in active">
<li class="media">
<div ng-repeat-start="item in items | limitTo: totalDisplayed as filteredResult">
{{item}}
</div>
<div class="panel panel-default" ng-repeat-end>
</div>
<div>
{{filteredResult.length}}
</div>
</li>
</ul>
<button ng-click="loadMore()" ng-show="totalDisplayed <= filteredResult.length">
more
</button>
However, I found filteredResult.length is displayed fine right after ng-repeat, but the button is never shown. If I try to display filteredResult.length in where the button is, it will show null.
So is there a rule for "as alias" scope? I've seen plenty of examples work but mine doesn't, what am I missing here?
EDIT:
The accepted answer uses controllerAs which indeed will resolve the problem. However, charlietfl's comment using ng-init to save filteredResult to parent scope is also very neat and that's the solution I use in my code eventually.
Probably some of classes in your <ul class="media-list tab-pane fade in active"> or <li class="media"> is selector for a directive that would have its own scope. So you store filteredResult in e.g. tab-pane's scope and then try to have access out of it's scope in outside of ul tag.
Try to use Controller as instead of scope:
angular
.module('plunker', [])
.controller('MainCtrl', function() {
vm = this;
// make an array from 1 to 10
var arr = [];
while (arr.length < 10) {
arr.push(arr.length + 1)
};
vm.items = arr;
vm.totalDisplayed = 5;
vm.filteredResult = [];
});
<body ng-controller="MainCtrl as main">
{{main.items}}
<ul class="media-list tab-pane fade in active">
<li class="media">
<div ng-repeat-start="item in main.items | limitTo: main.totalDisplayed as filteredResult">
{{item}}
</div>
<div class="panel panel-default" ng-repeat-end>
</div>
<div>
filteredResult = {{main.filteredResult = filteredResult}}
</div>
</li>
</ul>
<button ng-click="loadMore()" ng-show="main.totalDisplayed <= main.filteredResult.length">
more
</button>
</body>
http://plnkr.co/edit/drA1gQ1qj0U9VCN4IIPP?p=preview

How to trigger ng-click inside ng-repeat angularJS

ng-click does not work inside ng-repeat. I have read all the guides and similar questions, but nothing work in my code. If I click on the tag inside the ng-repeat nothing happen, but if I click on my button the function is called.
html
<div ng-repeat="sykdom in sykdommer" ng-model="sykdom.name" ng-click="test();">
<a class="item item-icon-right" href="#" ng-click="test();" >
{{sykdom.name}}
<i class="icon ion-ios-arrow-right"></i>
</a>
</div>
<button ng-click="test()> test </button>
JS
$scope.sykdommer = [{name:'test1'},
{name:'test2'},
{name:'test3'}];
$scope.test = function(){
alert('you clicked!');
};
I have tried with ng-click="$parent.test()" and ng-model="sykdom.name" without any luck. Please help, really stuck on this problem :(
Here is an example i've made with your data: http://plnkr.co/edit/o4sqPd
Template should be like:
<div ng-repeat="sykdom in sykdommer">
<a class="item item-icon-right" href="#" ng-click="test(sykdom.name);" >
{{sykdom.name}}
<i class="icon ion-ios-arrow-right"></i>
</a>
</div>
You should use ng-model directive only in case if it's a part of you changeable data - in input/textarea etc. tags (documentation: https://docs.angularjs.org/api/ng/directive/ngModel)

Animate rows through a directive inside a ng-repeat

I'm building this application in Angular where a div table is formed by using ng-repeat through the following html:
HTML
<div ng-repeat="(key, value) in data.data.ipv4">
<div class="cellbody">{{value.descr}}</div>
<div class="cellbody">{{value.protocol}}</div>
<div class="cellbody">{{value.internip}}</div>
<div class="cellbody">{{value.internrange}}</div>
<div class="cellbody">{{value.externrange}}</div>
<div class="deletecell">
<span class="toggledelete" ng-click="deleteport($event, key, 4)">
<i class="icon-minus negativehover"></i>
</span>
<span class="toggledelete" style="display:none">
<span>PORT DELETED</span>
<span class="deletedportundo" ng-click="restoreport($event, $index, 4)">
UNDO
</span>
</span>
</div>
</div>
The last div of each row, has a visible clickable button which sends a delete order to the server via the deleteport() function, and then, if everything goes all right, starts an animation where the whole cell is hidden and the previously hidden span with class 'deletedportundo' shows up.
Anyway the thing is my controller looks like this:
Angular Javascript Controller
$scope.deleteport = function(e,f) {
postData.index = f;
$http.post('serverside/router.php', postData)
.success(function(data, status, headers, config){
if (data.status == 'ok') {
var elem = angular.element(e.target);
$(elem).parent().parent().parent().children('.cellbody').hide('fast');
$(elem).parent().parent().children('.toggledelete').toggle();
$(elem).parent().parent().parent().children('.deletecell').animate({
width: "100%"
}, 300 );
$(elem).parent().parent().parent().children('.deletecell').addClass('macdeleted');
}
});
}
Which visually works as expected, except that I am aware that I should not be manipulating the DOM in the controller; I have been unsuccessfully trying to integrate this into a directive, but because every row is independent of the others I have not been able to achieve the desired effect.
Daniel,
There is nothing wrong with manipulating DOM in the controller. However, I can suggest an easier way to do it than to navigate with those nasty parent().parent().parent().parent().parent() ... calls :) .
Just add an ng-show to the toggledelete div, than just do $scope.portDeleted = false in your controller. This also works for the .cellbody tags.
As for the .deletecell class you can use ng-class, and just do $scope.deletecell = some_value.
<div ng-class="{deletecell:deletecell}">
<span class="toggledelete" ng-click="deleteport($event, key, 4)">
<i class="icon-minus negativehover"></i>
</span>
<span class="toggledelete" style="display:none">
<span>PORT DELETED</span>
<span class="deletedportundo" ng-click="restoreport($event, $index, 4)">
UNDO
</span>
</span>
</div>
For animate, you can use ngAnimate (http://www.nganimate.org/)

Resources