Call AJAX in Angular function - javascript

I have 2 problems with $http in function in this code.
$scope.buttonClick = function(){
// Send input data
$.post('lib/add_contact.php', $scope.contact, function(data){
data = angular.fromJson(data);
if(!data.error){
$scope.contact = "";
console.log('success');
}else{
console.log('error');
}
});
// Next Code
console.log('Next Code');
}
First problem is, when I want to clear a contact, it dont work immediately but only after I press key into input.
If I put
$scope.contact = "";
outside of POST, It works well.
Second problem is, why is POST called last? Output of code is
Code Next
success
but i would like that output be
success
Code Next
Thanks for ideas and answers.

You are using jQuery for the ajax call which is happening outside the angular world. You need to trigger a digest cycle for the scope to update so the change is reflected in your view.
To fix the issue:
$scope.buttonClick = function(){
// Send input data
$.post('lib/add_contact.php', $scope.contact, function(data){
data = angular.fromJson(data);
if(!data.error){
$scope.$apply(function(){ //<-- calling $apply()
$scope.contact = "";
});
console.log('success');
}else{
console.log('error');
}
});
// Next Code
console.log('Next Code');
}
However the preferred approach would to use the built in $http service included inside of angular which knows about angular and triggers the digest automatically.
Using $http (don't forget to add as a dependency to your controller)
$scope.buttonClick = function() {
$http.post('lib/add_contact.php', $scope.contact).success(function(data) {
data = angular.fromJson(data);
if (!data.error) {
$scope.$apply(function() { //<-- calling $apply()
$scope.contact = "";
});
console.log('success');
} else {
console.log('error');
}
});
}

Related

Angular show/hide working only on second function call, apply/digest issue?

I have the following in a controller, which is designed to show the content after it's loaded. Should in theory be pretty simple but is not playing ball, so I'm trying to understand what I'm doing wrong. This is for a view containing an ionic slide box which is what I'm trying to hide until the data is loaded, and with an ion-refresher for pull to refresh, hence the $scope.broadcast('scroll.refreshComplete');
//initial controller vars:
$scope.data = {};
$scope.data.loading = true;
$scope.data.user = 1;
$scope.data.week = {};
$scope.data.getContent = function() {
var now = new Date().getTime();
serverRequestFactory.getWeek(now)
.success(function(response){
$scope.data.week = response;
$scope.data.loading = false;
$ionicSlideBoxDelegate.update();
$scope.$broadcast('scroll.refreshComplete');
})
.error(function(response){
$scope.data.loading = false;
$scope.$broadcast('scroll.refreshComplete');
})
}
if($scope.data.user == 1){
//calls above on view load
$scope.data.getContent();
//$scope.data.getContent();
}
The weird thing is the above works if I uncomment the second call to $scope.data.getContent() but I don't know why. I've tried $scope.apply() both before and after setting the $scope.data.week object and the update of the slide box delegate. Where's my error?
EDIT: So I just added an ng-repeat directive to one of the slide box items:
<ion-slide-box on-slide-changed="slideHasChanged($index)" ng-hide="data.loading">
<ion-slide class="customSlidePadding" ng-repeat="item in data.week.items">
And now the entire slide box respects the initial ng-hide value and shows up without a second function call... There surely has to be an angular reason adding the directive to a nested item in the hidden slide box makes it work?
If you are using $scope.data.week on your view you should initialize it, otherwise angular does not create $watch over it after the first call.
Just do.
$scope.data.week = []; //or whatever data you want...
you should do this before calling the async request.
Unless your template is actually calling your function via an event handler
on-event="data.getContent()"
or via some binding mechanism (which I don't recommend)
<p>{{data.getContent()}}
Then you aren't actually calling this method. Nothing I've seen in the supplied code is actually calling this, you've only defined the method which calls itself in the if block.
Try explicitly calling it:
$scope.data.getContent = function() {
var now = new Date().getTime();
serverRequestFactory.getWeek(now)
.success(function(response){
$scope.data.week = response;
$scope.data.loading = false;
$ionicSlideBoxDelegate.update();
$scope.$broadcast('scroll.refreshComplete');
})
.error(function(response){
$scope.data.loading = false;
$scope.$broadcast('scroll.refreshComplete');
})
}
if($scope.data.user == 1){
//calls above on view load
$scope.data.getContent();
//$scope.data.getContent();
}
}
//explicitly calling
$scope.data.getContent();

Pass data between controllers while pageload without using $rootscope in angularjs

I have 2 controllers like below,
app.controller('ParentMenuController',
function ($scope,MenuService) {
$scope.contentLoaded = false;
$scope.showButton = false;
$scope.showButton = MenuService.getStatus();
});
Controller2:
app.controller('ChildMenuController',
function ($scope,MenuService) {
MenuService.setStatus(true);
});
Service:
app.factory('MenuService', function ($q,$http) {
var status= false;
var setStatus=function(newObj){
status=newObj;
};
var getStatus=function(){
return status;
};
return {
getStatus:getStatus,
setStatus:setStatus
};
});
I am not able to set the status to true, but the below line of coding is not at all executing, so the status is always false.
$scope.showButton = MenuService.getStatus();
On button click or any action from user i can trigger the event, but my requirement is while page load, the button should not be visible. When childMenu controller executes, then parent controller button should be visible. I dont want to use $broadcast which requires $rootscope.
Note: My controller and html has hundereds of lines. I just pasted here required code for this functionality. ChildMenuController(childMenu.html) has separate html and ParentMenuController(parentMenu.html) has separete html.
So $scope.showButton is not available in ChildMenucontroller. Both html is used as directive. Main html is index.html.
See this sample:
http://plnkr.co/edit/TepAZGOAZZzQgjUdSpVF?p=preview
You need to use a wrapper object so that it's properties are changed instead of the main object.
app.controller('ParentMenuController',
function ($scope,MenuService) {
$scope.contentLoaded = false;
$scope.showButton = MenuService.getStatus();
});
app.controller('ChildMenuController',
function ($scope,MenuService) {
MenuService.setStatus(true);
});
app.factory('MenuService', function ($q,$http) {
var status= {value:false};
var setStatus=function(newObj){
status.value=newObj;
};
var getStatus=function(){
return status;
};
return {
getStatus:getStatus,
setStatus:setStatus
};
});
It's exactly the same as your code, but state is now an object with a value property. The state object is always the same one so that when the value is changed in the service the changes are propagated to everyone that has ever requested that object.
Actually your service is getting called and this piece of line is executing
$scope.showButton = MenuService.getStatus();
but once your child controller got loaded you are only setting the status but in order to show button you should getStatus after setting it
Like this,
app.controller('ChildMenuController', function($scope, MenuService) {
MenuService.setStatus(true);
$scope.$parent.showButton = MenuService.getStatus();
});
this will set the button to true and it will be shown.
I have done a sample with your code please take a look into it
DEMO

GooglePlus login handle result

On my Polymer-Page I try to login to my google-plus page and retrieve my name and cover-photo url.
For that purpose I use a google-signin element that runs the following function when login finishes:
loginSuccess: function(e){
console.log("Success!");
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get( {'userId' : 'me'} );
request.execute(function(retr){
console.log("Data Loaded");
console.log(retr);
this.myFunction(retr);
});
});
}
Up to "Data Loaded" everything works great, also the console prints out the whole result object from google plus. But I am not able to execute functions (this.myFunction) or access data from polymer there. How do I store the result data from the gapi-request into my polymer variables???
Thanks, Malte
Inside the Ajax result, the this context is different. You need to setup a variable before your ajax request, or before your function to access it, like so :
self: this,
loginSuccess: function(e){
console.log("Success!");
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get( {'userId' : 'me'} );
request.execute(function(retr){
console.log("Data Loaded");
console.log(retr);
self.myFunction(retr);
});
});
}
I just found out, that i chose the wrong way to get my profile-data.
There is a polymer element to get data from the google-api called google-api-loader (http://googlewebcomponents.github.io/google-apis/components/google-apis/#google-api-loader). I use that element the following way additionally to the google-signin element:
<google-api-loader id="plus" name="plus" version="v1"
on-google-api-load="{{displayProfile}}">
</google-api-loader>
Here the function displayProfile
displayProfile: function(){
if (this.signedIn && this.$.plus.api) {
var request = this.$.plus.api.people.get({"userId": "me"});
request.execute(function (resp) {
this.userData = resp;
}.bind(this));
}
}
It works awesome and I get access to the data via this.userData. Here it another example: https://github.com/Scarygami/google-signin-samples/blob/master/elements/google-signin-status.html

ng-table not working for dynamic data

I have created a table and I am using http to load the data in the tables. So, in every click, my table data is changing, but I don't see the updated data in the table.
I had created a sample Plunker for the reference. In my project, WHen I click on Reload New Data, the data in table get's changed, but after 2-3 click it doesn't change. DId anyone know, how to fix it..
It is a problem with the ngTable directive. It updates only when data.length changes. Take a look at this plunk. I set $scope['tableParams1'] to null and inside the $timeout I set the new data. This forces angularJs to do a new cycle. So in the first cycle the ngTable sees the data.length changed to 0 and in the new cycle the ngTable sees the data.length changed again. If you don't use the $timeout, the ngTable will see that the data.length remains the same as before and won't do nothing.
With some trial and error I found a seemingly better solution to the issue than indicated in the plunkrs. For clarity, I am using $resource in a service to fetch my data. When I add a record via a modal, at first it wouldn't upload the ng-table after closing the modal. I figured out a way that works for me:
// Get data from factory
var data = dataFactory.query();
//Use promise to load data to table, note the replacing of the second tableParams
//object parameter with a function
data.$promise.then(function (data){
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10,
sorting: {
name: 'asc'
},
filter: {
name: undefined
}
}, resetTableParams()
);
});
//The function that replaces the tableParams param
var resetTableParams = function(){
return {
total: data.length,
getData: function($defer, params) {
var filteredData = params.filter() ? $filter('filter')(data, params.filter()) : data;
var orderedData = params.sorting() ? $filter('orderBy')(data, params.orderBy()) : filteredData;
params.total(orderedData.length);
$defer.resolve($scope.data = orderedData.slice((params.page() -1) * params.count(), params.page() * params.count()));
}
}
}
//A method to update the table that can be called when closing a modal
var updateTable = function(){
data = dataFactory.query();
data.$promise.then(function (data){
$scope.tableParams.reload();
});
}
// Add table row to table in modal and call updateTable() on closing modal
$scope.addRow = function(){
var modalInstance = $modal.open({
templateUrl: 'resources/partials/newrecord.html',
controller: NewRecordModalCtrl
})
modalInstance.result.then(function(){
updateTable();
});
}
Unfortunately, I can't give a clear explanation as to why this works and other methods don't. For instance, if you would not use the resetTableparams() function but leave it hardcoded, the table does not update. Somehow, Angular's digest cycle likes this better :) If somebody has a good explanation, please share!
You can directly use the provided method $scope.tableParams.reload();
I'm not sure about the exact cause of the incorrect incrementing, but the problem here may be more due to the approach. You should attach the count to the scope via $scope.count, and then use the ng-click directive to increment it: <button type="button" ng-click="count++;".
It would also make it easier for you/others to read and debug if you externalized the $scope.tableParams and the data from $scope.table1 conditional thing:
$scope.count = 0;
var dataCollections = [
[//.. first collection],
[//.. second collection],
[//.. third collection],
[//.. fourth collection]
];
$scope.data = dataCollections[0];
$scope.$watch('count', function () {
$scope.data = $scope.count < 4 ? dataCollections[$scope.count] : dataCollections[3];
});
I'm also not sure what you've got going on there with the $compile inside of the controller. It might make your task easier if you investigated some stuff about writing Angular controllers before delving into using a third-party module.
I was working on ng-tables with dynamic data as well (adding/removing),
I was using an ajax call to make changes to the database, and the success: function() {} property make changes to the tableParams
but changes wouldn't show on the page unless i refreshed it, with a few console.log()'s, I found out that the success: function() {} actually never executes
but there's another function that always executes, complete: function() {}
I know it's logically wrong to put the code that's supposed to work only after a successful call into complete: function() {} but if my success: function isn't working, this fix isn't that bad, especially knowing that the change is always successfully made to the database
it's strange because the success call works on other pages of the website, but it doesn't on some others.
EDIT:
well, this fix still doesn't solve the problem when the length of the data doesn't change "editing the text in the data" as mentioned above,, frustrating...
$.ajax({
type: "POST",
url: /*some url*/,
data: JSON.stringify({ /*some variable*/ }
}),
contentType: "application/json; charset=utf-8",
dataType: "Json",
success: function () { // would never execute even if it's a successful call
console.log("success");
},
error: function() { // optional, personally didn't try it
console.log("error");
}
complete: function () { //always executes regardless of the result
console.log("complete");
}
});
To solve the issue, make sure you have set the ng-controller="yourController" only once in your page.
Code below will not update data:
<div ng-controller="yourController">
<table ng-table = "tableParams" ng-controller = "yourController">
</table>
</div>
Solve the issue by removing extra ng-controller in your html page:
<div ng-controller="yourController">
<table ng-table = "tableParams">
</table>
</div>

Execute function once model loaded and content rendered in AngularJS

In my angular code, I load some data in ajax which fill my template, so far so good...
Then, once the data are loaded, I want to call a javascript function.
I couldn't find a way to have callback once the template is rendered. I have try several events but none are working.
My solution is to call the javascript method after a timeout:
$http.post('url').success(function (data) {
$timeout(function () {/* process data */ }, 200);
});
It seems to work, but that's a timeout, nothing guarantee me that at the end of the timeout everything is rendered. Maybe it is working only because my PC is fast...
Is there an event based solution? Or any solution better than this one...
The jsfiddle of my solution : http://jsfiddle.net/ZCT4K/5/
You need to $watch() your data. So you could try the following:
$http.post('url')
.success(function (data) {
$scope.postData = data;
});
//Watch the value of postData
$scope.$watch('postData', function () {
if ($scope.postData === undefined || $scope.postData === null || $scope.postData === "") {
return;
}
/*Else process your data*/
});

Categories

Resources