First of all a plunk: http://embed.plnkr.co/C866y3LHCE7QfBGuFQPQ/preview
So here I'm getting a set of posts via Ajax and displaying them using ngRepeat. Then (when the posts are done rendering) I want to scroll the page to a specific post (let's say post №18). Seems simple, but I can't get it working.
The problem seems to be that angular can't find that post after it receives the data from Ajax, so the position() and scrollTop() functions are not working.
You have to wait until after the view has been updated with your new model, use $timeout waiting 0 milliseconds to scroll immediately after the DOM is ready
plunkr
$scope.getPosts = function() {
$http.get(data_url).success(function(data){
$scope.posts = data;
$timeout(function() {
var post = $('#pid_18');
console.log('pid_18', post);
$('body').scrollTop(post[0].offsetTop);
}, 0);
});
I think the problem is that the render is not finish then if you have 2 element it's could be work but not with 1000.
So the best way is to know when the rendering is finish, a related post :
Calling a function when ng-repeat has finished
Related
I have two controllers one nested inside another. ParentController has one object which I am using in ChildController as it is directly available in child. Now, in child, I am calling $http service and then updating this object. I think it should update the view.
I tried calling $scope.$apply() promise success function, but I think I don't really understand how to use this one. How to update the DOM without refreshing the page?
I also read about calling $http service inside $scope.$apply(). How to do that?
$scope.addVideo = function (data) {
console.log('scope tutorial', tutorial);
$http.post('/tutorials/' + tutorial.id + '/videos/', $scope.formData)
.then((response) => {
$window.tutorial.videos.push(response.data.video);
window.location = '/tutorials' + tutorial.id;
})
}
Now, In the UI, I am using TutorialsController which takes care of adding Tutorial. I mean it's a form and it only works with form. Then there is VideoController that displays vidoes in this tutorial.
<div ng-controller="TutorialsController">
// add video in this tutorial logic
<ol ng-repeat="vid in tutorial.videos" ng-controller="VideoController">
<li>{{vid.title}}</li><button>Delete</button>
</ol>
</div>
When the video is added by parent, I want to update child and when child VideoController removes a video with a delete button, I want to remove it from the parent scope.
$scope.addVideo = function (data) {
console.log('scope tutorial', tutorial);
$http.post('/tutorials/' + tutorial.id + '/videos/', $scope.formData)
.then((response) => {
̶$̶w̶i̶n̶d̶o̶w̶.̶t̶u̶t̶o̶r̶i̶a̶l̶.̶v̶i̶d̶e̶o̶s̶.̶p̶u̶s̶h̶(̶r̶e̶s̶p̶o̶n̶s̶e̶.̶d̶a̶t̶a̶.̶v̶i̶d̶e̶o̶)̶;̶
$scope.tutorial.videos.push(response.data.video);
̶w̶i̶n̶d̶o̶w̶.̶l̶o̶c̶a̶t̶i̶o̶n̶ ̶=̶ ̶'̶/̶t̶u̶t̶o̶r̶i̶a̶l̶s̶'̶ ̶+̶ ̶t̶u̶t̶o̶r̶i̶a̶l̶.̶i̶d̶;̶
})
}
Considering you don't have full code written, I took some guesses and prepared something for you. You should try NOT to use $scope.$apply unless you are absolutely sure that you need it, most of the time angular will trigger digest cycle for you so there's no need to use $scope.$apply. Main thing is you could use $scope.$emit to emit value of video you want to delete.
If you are not familiar with $emit check this for more details (you could also check $broadcast which will send event from top to bottom - opposite of $emit):
https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$emit
So, in your case put in your VideoController something like:
$scope.deleteVideo = function(video) {
$scope.$emit('deleteVideo', video);
};
And in your TutorialsController you will need to listen to changes similar to this - data parameter will have your video object from child controller:
$scope.$on('deleteVideo', (event, data) => {
$scope.tutorial.videos = $scope.tutorial.videos.filter(video=>!angular.equals(video,data));
});
This above will just delete video from $scope - so you will need to to add your $http logic.
I simplified your video I added someProperty on each video object and title which you use to display title, right now everything is there just to show how you can easily communicate between controllers via $emit/$broadcast.
Here's that simplified example on fiddle: https://jsfiddle.net/pegla/d1rb96us/2/
Also I would advise you to check this Todd Motto's styleguide and start using component based architecture. It's currently best AngularJS style guide online: https://github.com/toddmotto/angularjs-styleguide
Hope it helps.
Cheers!
I'm looking for a pure angularJS way to call a controller method once a particular dom element is rendered. I'm implementing the scenario of a back button tap, so I need to scroll to a particular element once it is rendered. I'm using http://mobileangularui.com/docs/#scrollable.
Update: how my controller looks like:
$scope.item_ready=function(){
return document.getElementById($scope.item_dom_id).length;
};
$scope.$watch('item_ready', function(new_value, old_value, scope){
//run once on page load, and angular.element() is empty as the element is not yet rendered
});
Thanks
One hack that you could do and I emphasize hack here but sometimes it's just what you need is watch the DOM for changes and execute a function when the DOM hasn't changed for 500ms which is accepted as a fair value to say that the DOM has loaded. A code for this would look like the following:
// HACK: run this when the dom hasn't changed for 500ms logic
var broadcast = function () {};
if (document.addEventListener) {
document.addEventListener("DOMSubtreeModified", function (e) {
//If less than 500 milliseconds have passed, the previous broadcast will be cleared.
clearTimeout(broadcast)
broadcast = $window.setTimeout(function () {
//This will only fire after 500 ms have passed with no changes
// run your code here
}, 10)
});
}
Read this post Calling a function when ng-repeat has finished
But don't look at the accepted answer, use the 3rd answer down by #Josep by using a filter to iterate through all your repeat items and call the function once the $last property returns true.
However instead of using $emit, run your function...This way you don't have to rely on $watch. Have used it and works like a charm...
Background:
I am building my offline application which uses AngularJS for UI and PocuhDB for locally storing the data retrieved from the server.
Issue:
The data retrieved from PouchDB is not getting rendered in the UI.
Controller:
$scope.retrieveView = function (sys, code, majorVer, minorVer) {
var promise;
promise = dataService.getDataFromLocalDb().then(
function(dataFromPouchDb){
$scope.data = dataFromPouchDb.data;
});
return promise;
}
And then in the UI code I have the following :
<h1> {{data}}</h1>
I have debugged the code and everything seem to work fine. But the data is not getting displayed in the UI.
If I hard code a value to the data field then its getting rendered in the UI
$scope.data ="TEST";
This question is kind a old but I just came around it.
Issue is that Angularjs is based on so called digest cycles. When your model or view is changed digest cycle is triggered, watch for changes and update model or view respectively. It is so called two way data binding.
This digest cycle is not triggered periodically on some time base but on events instead. Those events are angular directives like ng-click, ajax calls $http or some other angular events like $timeout. You can find more information about digest here.
In general you should use those things when working with angular application to avoid such situations. In some cases its not possible however like in your case when getting data from DB. Digest cycle is not triggered and your view is not updated by angular.
Workaround for this is manually trigger $digest cycle. Way you have described:
if(!$scope.$$phase) {
$scope.$digest();
}
is working but considered as angular anti-patern and is discouraged by angular team, you should use:
$timeout();
instead. For more information see this answer.
I would maybe consider adding $timeout() call to hook for insert, update, delete hooks or events. Maybe pouchDB sync could be helpfull there.
The code you show seemed correct, maybe you can use console.log() to track the progress of the data. I think the problem might not in this layer. Maybe in the area where you wrapped getDataFromLocalDb(), track and find if the data have transfer to here, or where it disappeared.
The code started to work when i added the following :
if(!$scope.$$phase) {
$scope.$digest();
}
But i have no idea what magic does this code do.
It would be a great help if some some could advice.
The complete code that works now is :
$scope.retrieveView = function (sys, code, majorVer, minorVer) {
var promise;
promise = dataService.getDataFromLocalDb().then(
function(dataFromPouchDb){
$scope.data = dataFromPouchDb.data;
if(!$scope.$$phase) {
$scope.$digest();
}
});
return promise;
}
I found one weird issue while working in angular js
I am getting data using ajax call. I am binding data to $scope object but view is not getting updated after data bind
following is my code
$scope.getPlanDetail = function (){
$rootScope.planBody.checkUpdate= false;
$http.post('http://MyServerURL',JSON.stringify($rootScope.planBody)).
success(function(response){
$scope.dataVal = response;//Able to view data in console;
console.log($scope.dataVal)//data json shown in log window
localStorage.setItem("tempDataVal", JSON.stringify(response));//able to set data in localStorage;
}
}
getPlanDetail() function is getting called on btn click using ng-click
Same functionality I have done in other case(using get method.) where code is working properly. The only diff I found is that current AJAX call is taking to much of time because of too much of server side processing and its post method I am not sure whether this(using post method) is causing issue in binding
On same view(.html) I added dummy button ng-click event. After ajax success call I click on button and view is loaded because of data use from localStorage variable.
$scope.dummyClick= function(){
console.log($scope.dataVal);//giving Undefined
$scope.dataVal = JSON.parse(localStorage.getItem("tempDataVal"));// this time view binded properly.
}
I didn't understand why data is not bind to view in success method. Does the $scope time out after some time if server takes too much time to respond?
Thanks in Advance.
If you are changing the model inside an ajax call then you need to notify the angular js that you have some changes.
$scope.$apply(); after the below line will fix your issue. This line will update the scope.
$scope.dataVal = response;
Thanks,
Santyy
Im very new to AngularJS (4 hours new) and I'm trying to get an http call working, however what it seems like its happening is Angular keeps calling the http get request over and over again. I'm sure this is because my approach is wrong. This is what I'm trying to do.
snippet of my controller file The webservice works fine. I am running this in a node.js app
function peopleController($scope,$http){
$scope.getPeople = function(){
$scope.revar = {};
$http.get('/location/-79.18925/43.77596').
success(function(data){
console.log(data);
$scope.revar = data;
});
}
}
My list.html file
<div ng-controller="busController">
<div class="blueitem">{{getPeople()}}</div>
</div>
I know I will not see the results since im not returing anything in my getPeople Method but I wanted to see the log output of the result which I did see in chrome, but a million times and counting since angular keeps calling that url method over and over again. Instead it keeps hitting.
How do I get angular to return the response just once?
The problem you are experiencing is linked to the way AngularJS works and - to be more precise - how it decides that a template needs refreshing. Basically AngularJS will refresh a template based on a dirty-checking of a model. Don't want to go into too much details here as there is an excellent post explaining it (How does data binding work in AngularJS?) but in short it will keep changing for model changes till it stabilizes (no more changes in the model can be observed). In your case the model never stabilizes since you are getting new objects with each call to the getPeople() method.
The proper way of approaching this would be (on of the possible solutions):
function peopleController($scope,$http){
$http.get('/location/-79.18925/43.77596').
success(function(data){
$scope.people = data;
});
}
and then, in your template:
<div ng-controller="busController">
<div class="blueitem">{{people}}</div>
</div>
The mentioned template will get automatically refreshed upon data arrival.
Once again, this is just one possible solution so I would suggest following AngularJS tutorial to get better feeling of what is possible: http://docs.angularjs.org/tutorial/
Couple of things. Welcome to angularjs, its a great framework. You probably shouldn't be calling getPeople from the webpage. Instead,
function peopleController($scope,$http){
var getPeople = function(){
$scope.revar = {};
$http.get('/location/-79.18925/43.77596').
success(function(data){
console.log(data);
$scope.revar = data;
});
}
getPeople();
}
and then in html
<div ng-controller="busController">
<div class="blueitem">{{revar|json}}</div>
</div>
Also, I would recommend you looking into the ngResource, especially if you are doing CRUD type applications.
Hope this helps
--dan