I'm using the ocModal directive + service (https://github.com/ocombe/ocModal). When someone wants to delete a record they click the delete button which pops up, via ocModal, a modal to confirm.
oc-modal-close ng-click="deleteNote(id)"
So far so good. Within the controller deleteNote runs. Console.log shows the argument is the id I passed. I call an API to delete the note from my database and on a success call a function to delete the note from the Angular variable $scope.notes.
$scope.removeNote = function(id){
console.log(id);
console.log($scope.notes);
delete $scope.notes[id];
console.log($scope.notes);
};
The id is correct and the $scope.notes after the delete command shows it was correctly deleted. Yet, there is no corresponding update on my front-end. Following advise I've seen on Stackover I used apply() which led to a 'digest cycle already in progress' error. I then tried to use $timeout but while that got rid of the digest cycle error, it didn't solve the problem of the bind.
Is it relevant that the $scope.notes is used by a ng-repeat that then uses a directive? I've experimented a bit and don't think so, but just in case wanted to mention it.
Where are you calling $scope.removeNote from? Is it from your main controller or in the modal's controller? maybe a directive?
It feels like it is hitting the wrong scope. You can try to move notes to $scope.model.notes and see if it helps, as it will ensure the correct scope is referenced, but it's hard to say unless you try and provide a fiddle with the issue occurring.
I've created this simple fiddle trying to mimic an API call with $timeout and it works -> http://jsfiddle.net/7eqsc/1/
angular.module('myApp',[])
.controller('myCtrl',function($scope,$timeout){
$scope.notes={
a:'AngularJS',
b:'Rocks'
}
$scope.addRandom=function(){
$scope.notes[parseInt(Math.random()*10000).toString(36)]='New Item';
}
$scope.removeNote=function(id){
//emulate API call
$timeout(function(){
delete $scope.notes[id];
},2000);
}
});
In general, I recommend placing things under an object, such as ".model.yourArray", it ensures references are kept correctly and will save you a lot of trouble.
Related
This question might best be served by showing 2 JSFiddles examples (issue occurs in the 2nd JSFiddle link).
So I was playing with an example in Angular where I go through questions in my app, and when you get to a certain question called 'QS_SEARCH', then it autosubmits the question after 3 seconds.
I added some simple functionality so pretend now you are on the last question 'QS_SUCCESS' and if you clicked back to the 'QS_SEARCH' question, then the autosubmit timeout now gets initiated again... but if you click Back one more time quickly before the $timeout func runs, then when function executes it realizes the user isn't on 'QS_SEARCH' anymore and doesn't autosubmit. This is working correctly.
http://jsfiddle.net/armyofda12mnkeys/wxLqv4cs/
Now the same example with ui-router seems to hold the old $scope value and will still autosubmit even if you clicked Back to 'QS3' pretend, which is incorrect.
How can the $timeout get the updated scope so it doesn't autosubmit when you aren't on that specific question? I even tried $scope.$apply() inside the $timeout function but it still doesn't get the latest value.
http://jsfiddle.net/armyofda12mnkeys/qrp6cjgv/
Is this a Closure issue? I thought though $scope would be updated though.
Here is code for the $timeout:
if($scope.currentquestion.qid == 'QS_SEARCH') {
console.log('TIMEOUT: FUNC WILL START IN 3s');
$timeout(function(){
//$scope.$apply(); //update current question, not working though
console.log('3S DONE: FUNC EXECUTING');
console.log('current question is:'+ $scope.currentquestion.qid);
if( $scope.currentquestion.qid=='QS_SEARCH' ) {//double-check to make sure stilll on this question!!! (where issue occurs)
console.log('STILL on QS_SEARCH so autosubmit!');
$scope.getnextquestion();
}
}, 3000);
}
I'm converting a page in a mvc application with a lot of inline jquery javascript to angular using typescript.
The first calls works fine but I have a problem: based on a selection in a dropdown, the event updates several controls in the view, make a few ajax calls and finally update the view with the data from the calls.
Seems the conversion is working fine, but at the end of the call the page isn't updated.
I tried to remove all the old jquery code to avoid problems.
batarang and java console reports no errors.
the final ajax call is done and the result shown in a debug.
All seems to work fine, but the page isn't updated.
How can I find the problem?
thanks
Without seeing any code, it's difficult to answer but if you bind an event to an element and want to update something in the callback, you will have to use $apply
scope.$apply(function () {
// your code
});
$apply will trigger a $digest cycle, and should be used when you want to update something while being outside angular's context.
Most likely you are not handling your asynchronous calls correctly. It's impossible to tell from your question but it is a very common mistake with symptoms as you describe.
Make sure you are updating your model within the .then() method of a promise returned from an $http request. For example
someFnDoingHttpRequest().then(function(data){
// updated the model with the data
})
Also (another common mistake) make sure someFnDoingHttpRequest() returns a promise.
If you want to "find the problem" then you can use the following option.
Go to Internet Explorer (10 or 11).
Select "Internet Options" from the settings menu.
Go to the "Advanced" tab (the last tab)
Settings are listed and select "Display a notification about every script error"
Deselect the "Disable Script debugging (Internet Explorer)" and "Disable script debugging (Other)"
Run the program again, you will get notification about the real issue that happens while displaying actual result.
I've been following this tutorial http://draptik.github.io/blog/2013/07/28/restful-crud-with-angularjs/. I implemented a Grails backend with it instead of the Java one in the tutorial.
I've got the data coming back and forth, with one issue. If I create/update/delete a user, I don't see the changes reflected on my user list when I am redirected back. I have to refresh the page to see the updates.
Looking at the network traffic for an edit, it looks like it does a PUT and fires off the GET before the PUT is complete. Assuming this is because $resource returns a promise so things can be done asynchronously. So how do I handle this so that when $location redirects me, my list is up to date?
I'm guessing the options are to wait for the PUT to complete before redirecting/querying for the list, or to somehow manually manage the $scope.users to match the request?
Or maybe this tutorial is just a bad example? Maybe there is a better way to do it (still using $resource)?
Note: I've seen Restangular out there, and I've seen $http with success callbacks, but I would like to understand the situation above.
One way to overcome this issue would be to not redirect to the list page, till you get a callback, and then do a redirect. You can show some busy indicator till that time. The resource call looks like this.
resource.update(config,data,function() { //gets called on success},
function(error) { //gets called on failure});
In real life scenario waiting for the response of update makes sense as you want to handle the error and success scenarios on the same page.
I don't see your code anywhere so i'm just assuming (based on what you wrote and your current problem)
You are probably doing a full (or partial) get each time you changed a user and (re)binding the result to your scope. Doing this in the callback of the resource should actually start the digest cycle angular does to update modified objects. If you had been doing the fetching outside $resource - for example with custom/jquery ajax you would need to execute $scope.$apply()
What i really don't understand you would need to wait for the callback. You already know you added/modified a user. Instead of 'detaching' that user from your scope, modify it, post it to your rest server, then wait for callback, and reinserting it into the scope - why not modify it directly in the list/array you put on your scope?
var users = Users.get(function () {
$scope.users = users.record; // bind the resulting records to the scope
});
$scope.updateUser = function (user) {
resource.update(...); //pseudo
};
Then in your html, you will keep a reference to the currentUser and the div-list will update automaticly.
<div ng-repeat="user in users" ng-click="currentUser=user">{{user.Name}}</div>
<input ng-model="currentUser.Name">
<button ng-click="updateUser(currentUser);">Update</button>
If you don't want to see the update in the list while you type, but only once your callback fires or when you hit the button, would would instead use another ng-model for your input like this:
<input ng-model="tempUser.Name">
And you would then copy the value other in either the updateUser method or in the resource callback like this:
$scope.updateUser = function (user) {
user.Name = $scope.tempUser.Name; // should update automaticly
resource.update(...) // pseudo
}
Hope it helped!
For some reason when getData uses angular resource to bring the data it is being called twice, causing the resource to do it REST request twice too <--- bad...
Any idea why and how to solve it?
Here a working testcase/plunker example that recreates this scenario (look at the browser console - "getData being called...." displayed twice ) b.t.w as you can see I'm not really using the resource to bring real data, just to demonstrate the scenario, In my real app I do use the resource to bring real data and its being called twice just like in this example,
Thanks ahead
After looking into the src of the ng-table I noticed the following
$scope.$watch('params.$params', function(params) {
$scope.params.settings().$scope = $scope;
$scope.params.reload();
}, true);
Which means that the tables calls it 'getData' on count/filter/group/groupBy/page/sorting
which explains the behavior I was seeing.
When you call params.count(...) you ask ng-table to refresh data as you change page size. That's why you have two get-data calls.
If you don't want to have paging, then remove calls params.count and params.total.
If you need paging, then set page size and do not change it in getData.
This happened to me with a weird reason. getData get called twice on init (first load) only. changing page or sorting didn't call getData twice. The reason was that at init the ng-table directive was hidden in the template file.
Thank #Alexander Vasilyev. I understood my problem as you said. I want to explain a litte more here. In fact, the object "params" is the object configuration the table ng-table, then if "params" changed (ex: count or a property of the object), ng-table will invoke function getData() to refresh table.
In my case, I want to get information in the object "params" and change it but I dont want to refresh ng-table. I did it by cloning object "params" et work his object copied. Clone the object in JS with jQuery :
var resultParams = jQuery.extend(true, {}, params.$params);
And then, I will work on the object resultParams instead of "params" original.
I'm pretty new to Angular and I'm using firebase as my backend. I was hoping someone could debug this issue. When I first go to my page www.mywebsite.com/#defaultHash the data doesn't load into the DOM, it does after visiting another hash link and coming back though.
My controller is like this:
/* initialize data */
var fb = new Firebase('https://asdf.firebaseio.com/');
/* set data to automatically update on change */
fb.on('value', function(snapshot) {
var data = snapshot.val();
$scope.propertyConfiguration = data.products;
console.log($scope.propertyConfiguration);
console.log("Data retrieved");
});
/* save data on button submit */
$scope.saveConfigs = function(){
var setFBref = new Firebase('https://asdf.firebaseio.com/products');
setFBref.update($scope.propertyConfiguration);
console.log("configurations saved!");
};
I have 3 hash routes say "Shared", "Registration", and "Home" with otherwise.redirectTo set to "Shared".(They all use this controller) Here's the error that occurs: (all "links" are href="#hashWhereever")
1) Go to website.com/#Shared or just refresh. Console logs $scope.propertyConfiguration and "Data Retrieved". DOM shows nothing.
2) Click to website.com/#Registration, console logs $scope data properly, DOM is loaded correctly.
3) Click back to website.com/#Shared, console logs $scope data properly yet this time DOM loads correctly.
4) Refresh currently correctly loaded website.com/#Shared. DOM elements disappear.
Since $scope.data is correct in all the cases here, shouldn't Angular make sure the DOM reflects the model properly? Why is it that the DOM loads correctly only when I am clicking to the page from another link.
I can "fix" it by adding window.location.hash = "Shared" but it throws a huge amount of errors in the console.
FIXED:(sorta)
The function $scope.$apply() forces the view to sync with the model. I'd answer this question myself and close it but I'm still wondering why the view doesn't load correctly when I correctly assign a value to $scope. If Angular's "dirty checking" checks whenever there is a possibility the model has changed, doesn't assigning a value to $scope overqualify?
Angular has no way to know you've assigned a value to $scope.variable. There's no magic here. When you run a directive (ng-click/ng-submit) or Angular internal functions, they all call $apply() and trigger a digest (a check of the dirty flags and update routine).
A possibly safer approach than $apply would be to use $timeout. Currently, if you call a write op in Firebase, it could synchronously trigger an event listener (child_added, child_changed, value, etc). This could cause you to call $apply while still within a $apply scope. If you do this, an Error is thrown. $timeout bypasses this.
See this SO Question for a bit more on the topic of digest and $timeout.
This doc in the Angular Developer Guide covers how compile works; very great background read for any serious Angular dev.
Also, you can save yourself a good deal of energy by using the official Firebase bindings for Angular, which already take all of these implementation details into account.
Vaguely Related Note: In the not-too-distant future, Angular will be able to take advantage of Object.observe magic to handle these updates.