I am trying to write a simple app that does the following:
1. User inputs 2 parameters & clicks a button
2. Angular calls an external JAVA Servlet that returns JSON
3. App outputs the json string to the screen
I have a problem however, as when i click the button nothing happens. I believe (due to testing) that the reason this happens is that since the call is async, the variable that is returned is null.
Pertinent code:
controllers.js
function myAppController($scope,kdbService) {
$scope.buttonClick = function(){
var dat = kdbService.get($scope.tName,$scope.nRows);
$scope.data = dat;
}
}
services.js
angular.module('myApp.services', []).factory('kdbService',function ($rootScope,$http){
var server="http://localhost:8080/KdbRouterServlet";
return {
get: function(tname,n){
var dat;
$http.jsonp(server+"?query=krisFunc[`"+tname+";"+n+"]&callback=JSON_CALLBACK").
success(function(data, status, headers, config) {
console.log("1");
console.log(data);
dat=data;
}).
error(function(data, status, headers, config) {
alert("ERROR: Could not get data.");
});
console.log("2");
console.log(dat);
return dat;
}
}
});
index.html
<!-- Boilerplate-->
<h1>Table Viewer</h1>
<div class="menu" >
<form>
<label for="tName">Table Name</label>
<input id="tName" ng-model="tName"><br>
<label for="nRows">Row Limit</label>
<input id="nRows" ng-model="nRows"><br>
<input type="submit" value="Submit" ng-click="buttonClick()">
</form>
</div>
{{data}}
<!-- Boilerplate-->
When i execute the code and push the button, nothing happens. However, if i look in my log, i see this:
2
undefined
1
Object {x: Array[2], x1: Array[2]}
Clearly, what is happening is that the success function returns after the get function has returned. Therefore the object put into $scope.data is undefined, but the object returned from the jsonp call is left behind.
Is there a correct way to be doing this? Most of the tutorials I see assign the data to the $scope variable inside the success function, thereby skipping this problem. I want my service to be detached if possible.
Any help would be appreciated.
i would do something like that :
controller
function myAppController($scope,kdbService) {
$scope.kdbService = kdbService;
$scope.buttonClick = function(){
$scope.kdbService.get($scope.tName,$scope.nRows);
}
}
service
angular.module('myApp.services', []).factory('kdbService',function ($rootScope,$http){
var server="http://localhost:8080/KdbRouterServlet";
return {
data:{},
get: function(tname,n){
var self = this;
$http.jsonp(server+"?
query=krisFunc[`"+tname+";"+n+"]&callback=JSON_CALLBACK").
success(function(data, status, headers, config) {
console.log("1");
console.log(data);
self.data = data;
}).
error(function(data, status, headers, config) {
alert("ERROR: Could not get data.");
});
}
}
});
html
{{kdbService.data}}
OR
use continuation in the get method :
controller
function myAppController($scope,kdbService) {
$scope.buttonClick = function(){
kdbService.get($scope.tName,$scope.nRows,function success(data){
$scope.data = data;
});
}
}
service
get: function(tname,n,successCallback){
$http.jsonp(server+"?query=krisFunc[`"+tname+";"+n+"]&callback=JSON_CALLBACK").
success(function(data, status, headers, config) {
successCallback(data,status,headers,config);
}).
error(function(data, status, headers, config) {
alert("ERROR: Could not get data.");
});
}
OR use the $resource service
http://docs.angularjs.org/api/ngResource.$resource ( you'll need the angular-resource module
code not tested.
I want my service to be detached if possible.
then put the "data object" in a "data service" calling a "data provider service". You'll have to call the "data provider service" somewhere anyway. There is no skipping this problem in my opinion, since that's how javascript work.
also use angular.controller("name",["$scope,"service",function($s,s){}]);
so you will not need to care how parameters are called , as long as they are defined and injected properly.
Related
I want to bind some data with angular, I made an example and it works but I'm having problems to integrate my stuff into another app.
This is the controller of that app
angular.module('app', ['dcafe.navigation','ui.bootstrap'])
.controller('HeadlineReportController', function($scope, $http, $interpolate, $filter, datafactory, d3Factory, $timeout){
//code
$scope.SendData = function(){
$http.post('http://localhost:8080/xxx/xxx/', data, config)
.success(function (data, status, headers, config) {
$scope.PostDataResponse = data;
console.log("Success");
console.log("Status code: " + status);
})
.error(function (data, status, header, config) {
//$scope.ResponseDetails = "Data: " + data +
console.log("Error");
console.log("Status: " + status);
console.log("Headers: " + header);
});
};
$scope.SendData();
//MORE CODE
});
I was working with the SendData() function that was inside a controller, in my view I used ng-controller and then the ng-repeat, but things are different in the second app.
They call the controller at the begining of the view like this:
<span ng-controller="HeadlineReportController as vm">
so I tried to do the ng-repeat like in my workig app, like this:
<tr ng-repeat="data in PostDataResponse.result"> </tr>
But as you can see in the controller above the $scope.SendData = function() {}
is part of the HeadlineReportController so in this case I dont know how to do my ng-repeat, I was thinking in something like this:
ng-repeat="data in SendData()"
But It's not working.
If you are using controller as syntax change $scope to 'this'
var self = this;
self.SendData = function(){
$http.post('http://localhost:8080/xxx/xxx/', data, config)
.success(function (data, status, headers, config) {
self.PostDataResponse = data;
console.log("Success");
console.log("Status code: " + status);
})
.error(function (data, status, header, config) {
//$scope.ResponseDetails = "Data: " + data +
console.log("Error");
console.log("Status: " + status);
console.log("Headers: " + header);
});
};
self.SendData();
And use your view model as declared on controller as
<tr ng-repeat="data in vm.PostDataResponse.result"> </tr>
There are two ways of declare and use the controllers. From the ngController documentation
Two different declaration styles are included below:
one binds methods and properties directly onto the controller using
this: ng-controller="SettingsController1 as settings"
one injects
$scope into the controller: ng-controller="SettingsController2"
The second option is more common in the Angular community, and is
generally used in boilerplates and in this guide. However, there are
advantages to binding properties directly to the controller and
avoiding scope.
You will need to change the code in your controller to the following:
angular.module('app',['dcafe.navigation','ui.bootstrap']).controller('HeadlineReportController',
function($http,$interpolate,$filter,datafactory,d3Factory,$timeout){
//code
var vm = this;
$vm.SendData = function(){
$http.post('http://localhost:8080/xxx/xxx/', data, config)
.success(function (data, status, headers, config) {
$scope.PostDataResponse = data;
console.log("Success");
console.log("Status code: " + status);
})
.error(function (data, status, header, config) {
//$scope.ResponseDetails = "Data: " + data +
console.log("Error");
console.log("Status: " + status);
console.log("Headers: " + header);
});
};
vm.SendData();
});
And your ng-repeat will change to
ng-repeat="data in vm.SendData()"
They are using the Controller As syntax in the view when they say <span ng-controller="HeadlineReportController as vm">.
Controller As is something you should look in to; John Papa has a good explanation here.
From your view, you would then reference controller variables like vm.SendData(). Also, with Controller As, you will not have $scope variables in your controller.
Hello i am trying to display data from server in Angularjs App, where i got the data and use ng-repeat to display it through controller.
Here is how my controller looks like:
module.controller('FiveReasons', function($scope, $http, $rootScope, $sce) {
ons.ready(function() {
$scope.reasonsLists = {};
var reasonsListing = $http.get("http://vbought.com/design_14/index.php/design_ci/post/Clients");
reasonsListing.success(function(data, status, headers, config) {
console.log(data[0].post_title);
$scope.reasonsLists = data;
$scope.spinner = false;
});
reasonsListing.error(function(data, status, headers, config) {
alert("Can Not load the address Ajax");
});
});
});
But when the ng-repeat finish loading, the data is not displaying.
Here is how my ng-repeat is:
<ons-carousel swipeable overscrollable auto-scroll fullscreen var="carousel" name="FiveRes" class="FiveRes">
<div ng-repeat="reasonsList in reasonsLists">
<ons-carousel-item style="background: #09a4c0;" >
<div class="item-label">Number : {{reasonsList}}</div>
</ons-carousel-item>
</div>
<ons-carousel-cover></ons-carousel-cover>
</ons-carousel>
How can i now refresh the list once the data is there. Like we do "trigger("refresh")" in jquery.
After running ng-repeat, this is how i get the value but the screen is going blank.
Thank you! (In advance)
I think in this case I'd eliminate the use of ready and call your code using ng-init.
module.controller('FiveReasons', function($scope, $http) {
this.reasonsLists = {};
$scope.getReasonsLists = function() {
$scope.reasonsLists = {};
$http.get("http://vbought.com/design_14/index.php/design_ci/post/Clients")
.success(function(data, status, headers, config) {
console.log(data[0].post_title);
$scope.reasonsLists = data;
$scope.spinner = false;
})
.error(function(data, status, headers, config) {
alert("Can Not load the address Ajax");
});
};
});
then in your html on the table tag just add ng-init="getReasonsLists()"
Try this:
module.controller('FiveReasons', function($scope, $http, $rootScope, $sce) {
ons.ready(function() {
$scope.reasonsLists = {};
var reasonsListing = $http.get("http://vbought.com/design_14/index.php/design_ci/post/Clients");
reasonsListing.success(function(data, status, headers, config) {
console.log(data[0].post_title);
$scope.reasonsLists = data;
$scope.spinner = false;
});
reasonsListing.error(function(data, status, headers, config) {
alert("Can Not load the address Ajax");
});
//ADD this to notify Angular for the changes and run a digest cycle
$scope.$digest();
});
});
This is happening because the event (ons-ready) handler will not start a new $digest cycle.
To display the data without having to call $digest(), remove the ons-ready callback and execute your code directly in the controllers body. Like the example bellow:
module.controller('FiveReasons', function($scope, $http, $rootScope, $sce) {
$scope.reasonsLists = {};
var reasonsListing = $http.get("http://vbought.com/design_14/index.php/design_ci/post/Clients");
reasonsListing.success(function(data, status, headers, config) {
console.log(data[0].post_title);
$scope.reasonsLists = data;
$scope.spinner = false;
});
reasonsListing.error(function(data, status, headers, config) {
alert("Can Not load the address Ajax");
});
//No need to call $digest
});
UPDATE: Check this Codepen which is a simplified version of your code that shows only the issue. Remove $scope.$digest() from the comments to see the carousel working.
A "refresh" should not be necessary as angular uses a different approach. After certain events like "click" or the return of a $http-promise, angular goes through the binded variables and checks if they have changed - and if so - changes the DOM.
In your case I think the promise is returning before you add the success-callback. Try chaining it like:
$http.get("http://vbought.com/design_14/index.php/design_ci/post/Clients").success(function(data, status, headers, config) {
console.log(data[0].post_title);
$scope.reasonsLists = data;
$scope.spinner = false;
}).error(function(data, status, headers, config) {
alert("Can Not load the address Ajax");
});
In other cases you can use $scope.$apply() but I think this should tell you, that you can't nest $digests as there should be one going on after a $http-request.
Ok so I am experimenting with Angular but I have run into a problem and maybe it is because I haven't adopted the Angular way of thinking yet, but this is basically what I ran into.
I get some data with a JSON request and show this data in a list using ng-repeat. So far so good. No errors in the console, but it does not attach the eventlistener to the ng-repeat elements. The code is fine, because with non ng-repeat elements it works like a charm. Someone else ran into this problem and how did you solve it? Thanks in advance.
<div ng-controller="basictrl">
<h1>Lijst van producten</h1>
<ul ng-repeat="item in producten">
<li>{{item.naam}}</li>
<li>{{item.prijs}}</li>
</ul>
</div>
JS
angular.module("app", ['ngRoute'])
.controller("basictrl", function ($scope, $http, producteffecten) {
$scope.producten = null;
$http({
method: 'GET',
url: 'producten.json'
}).
success(function (data, status, headers, config) {
$scope.producten = data;
$scope.showdescription = producteffecten.showdescription;
}).error(function (data, status, headers, config) {});
})
.factory('producteffecten', function() {
var effecten = {};
effecten.showdescription = $('ul').hover(function (){
$(this).append("<p>Test</p>");
});
return effecten;
})
The simple answer is: you can use $('ul').on("hover",function(){}). But this is not the angular way of thinking. The first improvement you could make is this:
add ng-mouseover="muisOverEffectje()" to the ul in the html
add $scope.muisOverEffectje=function(){ your event code} to the angular page
The next step to avoid adding the code with jquery to make it even more angular could be something like this:
add a parameter which gives the selected item to your event : muisOverEffectje(item)
add ng-show="showDescription" to the the ul
put show-description to true in the event handler
The last step could be: Make a directive for you . Like a product component for example.
Setting a timeout works, but it is hacky I guess. I will try to rewrite in a more angular way.
.factory('producteffecten', function() {
var effecten = {};
//SETTING TIMEOUT WORKS SOMEHOW
effecten.showdescription = setTimeout(function() {
$('ul').hover(function (){
$(this).append("<p>Test</p>");
});
}, 10);
return effecten;
})
You could also write an directive. Which is the nicest way in angular to do this kind of stuff:
Html:
<div ng-controller="basictrl">
<h1>Lijst van producten</h1>
<ul ng-repeat="item in producten" hover-text="item.effect">
<li>{{item.naam}}</li>
<li>{{item.prijs}}</li>
</ul>
</div>
js:
.controller("basictrl", function ($scope, $http, producteffecten) {
$scope.producten = null;
$http({
method: 'GET',
url: 'producten.json'
}).
success(function (data, status, headers, config) {
$scope.producten = data;
$scope.showdescription = producteffecten.showdescription;
}).error(function (data, status, headers, config) {});
})
.directive("hoverText",function(){
return {link:function(scope,elem,attr){
var insertElem=$("<div class='hovertext'>"+scope.hoverText+"</div>")
elem .mouseenter(function() {
insertElem.appendTo(elem);
})
.mouseleave(function() {
insertElem.remove();
});
},
scope:{"hoverText":"="}
};
});
I'm trying to get which data has been changed in array. my use case is First time all data fetch from ajax and show within row and fetch new data using $http every 5 second. I need to know that if the new data is same as old one. If yes then which value has changed so I've to update that background to some color..
What I've already tried
var app = angular.module('app', []);
app.controller('listingController', function($scope, $http, $timeout){
$scope.listings;
setInterval(function(){
$http.get('_includes/ajax.php?req=get').
success(function(data, status, headers, config) {
$scope.listings = data;
console.log(data);
}).
error(function(data, status, headers, config) {
// log error
});
}, 5000);
});
app.controller('RentalDateController', function($scope, $log){
console.log($scope.listings);
$scope.$watch('listings.Third_Column', function (Third_Column, oldvalue) {
//$log.info(Third_Column, $scope.listings.First_Column);
console.log('being watched oldValue:', oldvalue, 'newValue:', Third_Column);
}, true);
});
My html is
<body ng-app="app">
<div ng-controller="listingController">
<table>
<tr>
<th>Testing</th>
<th>Pripse</th>
</tr>
<tr ng-repeat="row in listings" ng-controller="RentalDateController">
<input type="text" ng-model="row.Third_Column">
<input type="text" ng-model="row.First_Column">
<th>{{row.Third_Column}}</th>
<th>{{row.First_Column}}</th>
</tr>
</table>
</div>
</body>
I think I need to use $watch but it's not working.
You have the angular two-way data binding so it should automatically update your ng-repeat when the model changes.
I suggest the following
1) Remove RentalDate controller first.
2) Use $timeout, and on success of http use this
$scope.$apply(function(){
$scope.listing = data;
});
If that doesn't still automatically update, put the array in an object.
$scope.data = {}
$scope.data.listings = putArrayHere();
This will work because of this. Read up. :D
https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance
Try doing this:
var app = angular.module('app', []);
app.controller('listingController', function($scope, $http, $timeout){
$scope.listings;
var previousListing = [];
var newData;
setInterval(function(){
$http.get('_includes/ajax.php?req=get').
success(function(data, status, headers, config) {
$scope.listings = data;
console.log(data);
}).
error(function(data, status, headers, config) {
// log error
});
if( previousListing.length == $scope.listings.length )
{
console.log('No new data available.');
}
else
{
// length of the arrays are different, hence new data must be available
console.log('New data available!');
newData = $scope.listings.splice( 0, ($scope.listings.length - previousListing.length) ); // returns new data in the form of an array
console.log(newData);
previousListing = $scope.listings; // reset previous data variable
}
}, 5000);
});
I have a REST service that I made which returns a json string which is simply a set of strings (I used Gson to generate this string (Gson.toJson(mySetOfStrings))
So I have added to my index.html:
<div ng-controller="ListOptionsCtrl">
<form novalidate>
<button ng-click="refreshList()">refresh</button>
<select name="option" ng-model="form.option" ng-options="o.n for o in optionsList></select>
</form>
</div>
and in my script:
var ListOptionsCtrl = function ListOptionsCtrl($scope, $http) {
$scope.refreshList = function() {
$http({
method: 'GET'
url: '*someurl*'
}).
success(function(data) {
$scope.optionsList = angular.fromJson(data);
});
};
}
Unfortunately all this produces in my select box is an empty list. When I see what the response to the GET request is it returns a json string with content in it so I do not see why nothing is being added to this element. What am I doing wrong here? thanks
It is because Angular does not know about your changes yet. Because Angular allow any value to be used as a binding target. Then at the end of any JavaScript code turn, check to see if the value has changed.
You need to use $apply
var ListOptionsCtrl = function ListOptionsCtrl($scope, $http) {
$scope.refreshList = function() {
$http({
method: 'GET'
url: '*someurl*'
}).
success(function(data) {
$scope.$apply(function () {
$scope.optionsList = angular.fromJson(data);
});
});
};
}
Try this.
More about how it works and why it is needed at Jim Hoskins's post
You should check for $digest error by doing if(!$scope.$$phase) { ... } before doing $apply.
success(function(data) {
if(!$scope.$$phase) {
$scope.$apply(function () {
$scope.optionsList = angular.fromJson(data);
});
}
});