I'm new to Angular (few hours new). I'm getting pretty much what I want, by adjusting the demo. But i can't get my AJAX request to work.
I tried various solutions, but on gets in an endless loop (figured out that's the way how Angular works). In an other solution nothing really happens..
My current solution (tried to place the peopleController about everywhere):
Controller:
app.controller('MainController', ['$scope','$http', function($scope,$http) {
//$http is working in this
var scrollItems = [];
for (var i=1; i<=100; i++) {
scrollItems.push('Item ' + i);
}
$scope.scrollItems = scrollItems;
function peopleController($scope,$http){
// Simple GET request example :
$http.get('/public/ajax.php').
success(function(data, status, headers, config) {
console.log("worked");
// this callback will be called asynchronously
// when the response is available
scope.people = data;
}).error(function(data, status, headers, config) {
console.log("fail");
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
}]);
HTML:
<div ng-controller="peopleController">
{{people}}
</div>
But it gives me this error:
Error: [ng:areq] http://errors.angularjs.org/1.3.0/ng/areq?p0=peopleController&p1=not%20aNaNunction%2C%20got%20undefined
at Error (native)
at http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:6:416
at Mb (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:19:510)
at nb (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:20:78)
at $get (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:74:494)
at http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:56:415
at r (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:7:408)
at M (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:56:281)
at g (http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:51:201)
at http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js:50:309
Hope someone can help me out here :)
Other solutions i tried for example
match the controller name in your html and js file, if it's 'peopleController' in you html file name it 'peopleController' in the controller rather than 'MainController'.
than change the line
function peopleController($scope,$http){
to
function peopleController(){
you use dependency injection in the controller function, not on the contained functions, the contained function already have access to $something because they are under the controller function's scope
Your view must refer to the controller you declared, which is MainController:
<div ng-controller="MainController">
{{people}}
</div>
Inside your controller, set people to [] and bound to $scope, remove the parameters you passed in peopleController and initiate the request. Caution: in the success handler, rename scope to $scope.
$scope.people = [];
peopleController(); //initiate your ajax request
function peopleController(){ //remove the parameters
// Simple GET request example :
$http.get('/public/ajax.php').
success(function(data, status, headers, config) {
console.log("worked");
// this callback will be called asynchronously
// when the response is available
$scope.people = data; //scope -> $scope
}).
error(function(data, status, headers, config) {
console.log("fail");
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
peopleController() is misleading; its purpose is to get data. I'd recommend to rename it to getPeople().
If you want a separate controller called peopleController you need to define it separately as follows:
app.controller('MainController', ['$scope','$http', function($scope,$http) {
//$http is working in this
var scrollItems = [];
for (var i=1; i<=100; i++) {
scrollItems.push('Item ' + i);
}
$scope.scrollItems = scrollItems;
}]);
app.controller('peopleController', ['$scope','$http', function ($scope,$http){
// Simple GET request example :
$http.get('/public/ajax.php').
success(function(data, status, headers, config) {
console.log("worked");
// this callback will be called asynchronously
// when the response is available
$scope.people = data;
}).
error(function(data, status, headers, config) {
console.log("fail");
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
}]);
You need to remove $scope and $http from peopleController function which are overriding the existence of $http & $scope
Other thing you should mention ng-controller='MainController' instead of ng-controller='peopleController'
Also change the name peopleController to getPeople. I'm assuming that you want function there instead of controller.
Related
So far studying existing stackoverflow answers always helped me along, and I could always find an answer, but now I'm really stuck.
I'm building an app, which uses a directive to create calender month type boxes.
<app2directive class="column_50" jahr="2016" mon="November"></app2directive>
the directive code therefore isolates the scope and utilizes a templateUrl file to draw the calendar month
App.directive('app2directive',function( YEARS, MONTHS, DAYS){
return {
restrict:"ACE",
scope:{},
replace:true,
templateUrl:"templates/cal_directive3.html",
controller: function ( $scope, $attrs, $injector, $log, YEARS, MONTHS, DAYS) { var factoryName = "datumFactory" ;
var factoryTonnen = "jsonsrc" ;
var factoryInstance = $injector.get( factoryName ) ;
var tonnenInstance = $injector.get( factoryTonnen ) ;
var wtf = $scope.jsondata.get().then( function( promise){
console.log( "jsondata", promise ) ;
//$scope.tonnen = promise ;
}, function( error){
console.log( "error", error ) ;
}) ;
});
At the moment i use an $injector to inject a factory which runs a $http-request to read a json-file with data such as holidays or other static information specific to the chosen year and month(s).
App.factory( 'jsonsrc', function( $http, $q ){
return {
get: function(){
var deferred = $q.defer() ;
$http.get( 'termine_2016.json' )
.success(function(data, status, headers, config) {
deferred.resolve( data ) ;
}).error(function(data, status, headers, config) {
console.log( "[jsonsrc] request didnot work!" );
deferred.reject( data ) ;
}) ;
return deferred.promise ;
}
}
});
The effect of it is, that the same call to $http is run 12 times for a full year page load.
My concern is to refactor the file, so that I could preferably load the json data into the main-controller and the directive scope could inherit from the parent scope.
By its nature the call returns a promise. The directive would need means to wait for that promise to resolve before it should proceed, but right now I'm stuck on how to go about it. Thanks in advance for any pointers!
First $http is already returning a promise you can do :
return $http.get(...)
PS : you can chain promise so if you have some preprocessing to do you can do
return $http.get(..).then(function(response){
var data = response.data;
[..process..]
**return data;**
}, function(rejection){
// do some stuff
});
Second : Usually you bind data to your directive (ng-model for instance), and call services in controller (the view controller i mean). In order to handle the asynchronous loading of data you use the scope.$watch or attrs.$observe on the model to refresh your directive with the data loaded.
This enforces not to bind the directive with how your data loaded making them reusable, whatever the way you load your data or change it on your application.
Note what I put in bold, you musn't forget that or the next call to then won't have your processed data.
Finally : usually the link function provided by directive API you can just have :
link : function(scope, element, attrs){
attrs.$observe('myVariable', function(){
// refresh your internal state of the directive here
});
}
'myVariable' meanning in the call of your directive you have an attr my-variable :
and "myData" is loaded in the view controllerlike this :
jsonrc.get().then(function(response){
$scope.myData = response.data;
});
If you want to go further I suggest you to build a service for your holidays service so you load the data only at startup of your application :
App.service('MyService',['$http', function($http){
var promise = $http.get([URL]);
return function(){ // this is your service
var me = this;
promise.then(function(response){
this.data = response.data;
});
}
}]);
So now you can use in your main controller : scope.data = MyService.data; or even use it in your directive, or use some getter if you want, this is usually better but not always revelant.
If i forget anything tell me.
I think this could help.
First add the async call into a parent controller (directive's parent
controller).
Then isolate your directive's scope. And add a model to it.
scope: {
data: '=',
}
On your controller add a variable like: $scope.directiveData = {}
Assign the value of that variable with the result of the async call.
Obviously pass it to your directive: <mydirective data="directiveData"></mydirective>
Use this variable on your template with the dataname, or scope.dataon link.
Probably you will need mocked data for directiveData, or just add an ng-if to prevent crashing (when trying to show data the first time, and the directiveData is empty object).
Have your factory save the httpPromise and create it only once.
App.factory( 'jsonsrc', function( $http ){
var httpPromise;
function load () {
httpPromise = $http.get( 'termine_2016.json' )
.then(function onFullfilled(response) {
//return data for chaining
return response.data;
}).catch( function onRejected(response) {
console.log( "[jsonsrc] request didnot work!" );
//throw to reject promise
throw response.status;
});
return httpPromise;
};
function get () {
if (httpPromise) return httpPromise;
//Otherwise
return load();
};
return { load: load,
get: get
};
});
Notice that I removed the $q.defer and instead used the .then and .catch methods. The .success and .error methods have been deprecated; see the AngularJS $http Service API Reference -- deprecation notice.
You can then simplify your directive:
App.directive('app2directive',function(){
return {
restrict:"ACE",
scope:{},
replace:true,
templateUrl:"templates/cal_directive3.html",
controller: function ($scope, $attrs, jsonsrc, $log, YEARS, MONTHS, DAYS) {
jsonsrc.get().then (function (data) {
$scope.tonnen = data ;
}).catch ( function(error){
console.log( "error", error ) ;
});
})
}
});
Implemented this way, the jsonsrc factory executes the XHR GET only once, but each instantiation of the app2directive can retrieve the data for its own isolate scope.
In PHP side there are some element of array:
$this->data['messages']['ms'][] = 'Line1';
$this->data['messages']['ms'][] = 'Line2';
and method that to return json format:
echo json_encode($this->data['messages']); die();
Angular side:
$scope.response = {};
....
request.success(function (data) {
$scope.response.message = data.messages; // Edit here
});
<div ng-repeat="error in response">{{error}}</div>
When I try to get array items, I get nothing
I have converted my difficult object to array:
$scope.messages = [];
...
// In loop
$scope.messages.push(item);
Output in console:
Array[2]0: "Qeydiyyat mümkün deyil. Bu e-mail ilə istifadəçi artıq saytda qeydiyyatdan keçib"1: "Bu IP ünvanından artıq qeydiyyatdan keçilib"
In HTML template I try to display elements:
<div ng-repat="error in messages">{{error}}</div>
you must check your request. Are you useing $http?
Have you provided http in controller/service?
// Simple GET request example :
$http.get('/someUrl').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
you may also check with console.log(data) if theres any data provided.
Maybe there's different construction than you thought (for example wrapped data)
P.S. it should be messages (with s in the end)
Try this in your angular side.
$scope.response = {};
....
request.success(function (data) {
$scope.message = data.data;
console.log($scope.message);
});
<div ng-repeat="error in message">{{error}}</div>
You call your variable in a wrong way. Try console log the $scope.message if there are data's provided.
I am new to angular and what I am willing to do is replace a piece of code I wrote in the past in jquery to angularjs.
The goal is to take a string from a span element, split it in two and pass the two strings as parameters in a GET request.
I am trying to learn best coding pratices and improving myself so any comments of any kind are always welcome.
Working Code in jquery:
//Get Song and Artists
setInterval(function () {
var data = $('#songPlaying').text();
var arr = data.split('-');
var artist = arr[0];
var songTitle = arr[1];
//Request Lyrics
$.get('lyricsRequester.php', { "song_author": artist, "song_name" : songTitle},
function(returnedData){
console.log(returnedData);
$('#refreshLyrics').html(returnedData);
});
},10000);
Code in Angular
var app = angular.module("myApp", []);
app.factory('lyricService', function($http) {
return {
getLyrics: function($scope) {
//$scope.songArr = $scope.currentSong.split('-'); <-- **undefined currentSong**
//$scope.artist = $scope.songArr[0];
//$scope.songTitle = $scope.songArr[1];
return
$http.get('/lyricsRequester.php', {
params: {
song_author: $scope.artist,
song_name: $scope.songTitle
}
}).then(function(result) {
return result.data;
});
}
}
});
app.controller('lyricsController', function($scope, lyricService, $interval) {
$interval(function(){
lyricService.getLyrics().then(function(lyrics) {
$scope.lyrics = lyrics; <-- **TypeError: Cannot read property 'then' of undefined**
console.log($scope.lyrics);
});
}, 10000);
});
index.html (just a part)
<div class="col-md-4" ng-controller="lyricsController">{{lyrics}}</div>
<div class="col-md-4"><h3><span id="currentSong" ng-model="currentSong"></span></h3><div>
You need to be careful with your return statement when used in conjunction with newlines, in these lines:
return
$http.get('/lyricsRequester.php',
If you don't, JS will automatically add a semicolon after your return, and the function will return undefined.
Move the $http.get statement to the same line as your return statement.
return $http.get('/lyricsRequester.php', ...
Refer to the following docs:
MDN return statement
Automatic Semicolon Insertion
As for your second issue, you $scope is not really something you inject into your services (like $http). Scopes are available for use in controllers.
You need to refactor your code a bit to make things work.
eg. Your getLyrics function can take a song as a parameter. Then in your controller, you call lyricsService.getLyrics(someSong). Scope access and manipulation are only done in your controller.
app.factory('lyricService', function($http) {
return {
getLyrics: function(song) {
var songArr = song.split('-');
var artist = songArr[0];
var songTitle = songArr[1];
return $http.get('/lyricsRequester.php', {
params: {
song_author: artist,
song_name: songTitle
}
}).then(function(result) {
return result.data;
});
}
}
});
app.controller('lyricsController', function($scope, lyricService) {
$scope.currentSong = 'Judas Priest - A Touch of Evil';
$interval(function(){
lyricService.getLyrics($scope.currentSong).then(function(lyrics) {
$scope.lyrics = lyrics;
console.log($scope.lyrics);
});
}, 10000);
});
You also have some other issues, like using ng-model on your span. ng-model is an angular directive that is used in conjunction with form elements (input, select etc.), not a span as you have. So you might want to change that into an input field.
$http does not use .then, it uses .success and .error. the line that you have where it says then is undefined, should be replaced with a success and error handler instead. Below is a sample from the docs:
// Simple GET request example :
$http.get('/someUrl').
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
See Link:
https://docs.angularjs.org/api/ng/service/$http
I want to get carData.json file from a server using AngularJS.
Here is how I have structured it:
I have a services.js file (inside of js forlder) where I hold all of my services and factories. Here is the factory that I use to get carData.json file from a server:
carApp.factory('getAllCars', function($http){
return {
get: function() {
return $http.get('data/carData.json');
}
};
});
I also have a CarsByReviewCtrl controller that uses the carData.json file for its purposes:
carApp.controller("CarsByReviewCtrl", function($scope, getAllCars) {
getAllCars.get().success(function(data){
$scope.allCars = data;
}).error(function(data, status, headers, config) {
alert("AJAX failed")
});
$scope.carList = [];
console.log($scope.allCars);
...
And finally here is the end of my .html file where I pass these .js files. (I have called the controller in the middle of my html file)
<script type="text/javascript" src="js/controllers/CarsByReviewCtrl.js"></script>
<script type="text/javascript" src="js/services.js"></script>
</body>
</html>
Now, if I run my app and open the console, I would get the output of undefined, instead of javascript object that I got from the server.
What have I done wrong and how can I fix that?
You are trying to print the content of $scope.allCars before the HTTP request is resolved.
Added some comments to your code in order to explain how you should be reading it:
carApp.controller("CarsByReviewCtrl", function($scope, getAllCars) {
// first line of JS to be invoked
getAllCars.get().success(function(data){
// this will be executed later in time, after receiving the HTTP response (case success)
$scope.allCars = data;
}).error(function(data, status, headers, config) {
// this will be executed later in time, after receiving the HTTP response (case error)
alert("AJAX failed")
});
// this will be executed immediately after the previous JS line: getAllCars.get()
$scope.carList = [];
// this will be executed immediately after the previous JS line
console.log($scope.allCars);
the problem is: console.log($scope.allCars) runs before the success handler runs.
you can change your code to:
carApp.controller("CarsByReviewCtrl", function($scope, getAllCars) {
getAllCars.get().success(function(data){
$scope.allCars = data;
console.log($scope.allCars);
}).error(function(data, status, headers, config) {
alert("AJAX failed")
});
$scope.carList = [];
...
I'm trying to create a simple form with Angular, and need to render server-side validation errors. Our REST API handles validation errors with a 422 status code and a JSON array of errors in the response body.
My Controller:
.controller('MyController, function($scope, $http) {
$scope.save = function() {
var promise = $http.post('http://myapi.net/resources', $scope.data)
.success(function(data) {
// Success
})
.error(function(data, status) {
$scope.errors = data.errors;
});
$scope.errors = [];
return promise;
};
});
My Template:
<span ng-repeat="e in errors" class="error">
{{e.field}} - {{e.message}}
</span>
The errors render correctly after the first POST, but if a second POST fails, the errors sent by the server are appended in the DOM. The previous batch of errors remains, making it appear that no improvements to the data have been made. Stepping through the code, the $scope variable has the correct data, but the DOM does not. Is there a way to force ng-repeat to destroy all its previous DOM and build new ones? Or is there a better way to debug this so I can see what is going on?
As #tymeJV said it should just work. How about resetting the errors array using an empty array:
myApp.controller('MyController', function($scope, $http) {
$scope.save = function() {
// always a good idea
$scope.errors = [];
// ...
});
Here's also a plunk, maybe you can recreate it there.
Well I don't know what's going on with ng-repeat, but after 2 days I've discovered a workaround. Using a second scope variable and a watch seems to restore the correct behavior:
.controller('MyController, function($scope, $http) {
$scope._errors = [];
$scope.$watch('_errors', function(val) {
$scope.errors = val;
});
$scope.save = function() {
var promise = $http.post('http://myapi.net/resources', $scope.data)
.success(function(data) {
// Success
})
.error(function(data, status) {
$scope._errors = data.errors;
});
$scope.errors = [];
return promise;
};
});
Why this isn't necessary in the success callback is beyond me, but there you go.