AngularJS $http reading JSON data - javascript

How can i display data from an array of JSON data returned from the server in AngularJS? This is what i have, and for some reason i cannot display any data on my html page:
<script>
function placesCtrl($scope, $http) {
$http.get("http://somedomain/api/places_JSON.php")
.success(function(response){
$scope.names = response;
});
$scope.display = $scope.names[2].City;
}
</script>
The JSON data that the $http.get is returning looks something like this:
[{City: "Berlin", Country: "Germany"},
{City: "Portland", Country: "USA"},
{City: "Barcelona", Country: "Spain"},
{City: "Paris", Country: "France"},
{City: "Cowes", Country: "UK"}]
Then the HTML code looks something like this:
<!DOCTYPE html>
<html>
<body>
<div ng-app="" ng-controller="placesCtrl">
<p> {{display}}</p>
</div>
So instead of displaying "Barcelona" as the result, it just displays {{display}}.
Any suggestions would be much appreciated.

Your problem is that the success callback is called after you set the scope variable. Move the variable assignment inside your callback function, and you should be fine.
Also, as ryeballar points out, it's highly recommended that you as you register an application in the ng-app directive. See the tutorial for details. Although you don't need it for a simple example like this, it will make your life much, much easier as you add more components.

You have to register your Controller. Try this:
var myApp = angular.module('myApp',[]);
myApp.controller('placesCtrl', ['$http','$scope', function($http, $scope) {
$http.get("http://somedomain/api/places_JSON.php")
.success(function(response){
$scope.names = response;
});
$scope.display = $scope.names[2].City;
}]);
And edit your html:
<div ng-app="myApp" ng-controller="placesCtrl">

Related

Use child scope's data for parent scope's ng-repeat

Update 2 added, see below
First of all, this is the starting point of the framework I am working with (and needs to fix):
// index.html
<!doctype html>
<html ng-app="myApp">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="index.js"></script>
<body>
<div ng-controller="outerController">
<div id="copy"></div>
<hr>
<div id="src" ng-controller="innerController">
<table>
<th>Name</th>
<th>Type</th>
<tbody>
<tr ng-repeat="poke in pokemon">
<td>{{ poke.name }}</td>
<td>{{ poke.type }}</td>
</tr>
<tr>
<td>Pikachu</td>
<td>Electric</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
// index.js
var app = angular.module("myApp", []);
app.controller("innerController", function($scope) {
$scope.pokemon = [{
name: "Bulbasaur",
type: "Grass/Poison"
}, {
name: "Charmander",
type: "Fire"
}, {
name: "Squirtle",
type: "Water"
}];
});
app.controller("outerController", function($scope) {
$("#copy").html($("#src").html());
});
So as you can see, the child controller will generate a table from its scope's data via ng-repeat. This step is successful. The next step is for the parent controller to copy-paste the inner HTML from src to copy. The intent is for copy to contain a copy of the complete table fully generated by angularJS inside src.
This step has failed. Only the table headers and the static Pikachu row is visible. After doing some research I am certain that this is because pokemon is inside the child controller's scope which is inaccessible by the parent controller. The HTML copied into the copy container includes the entire ng-repeat directive. This copied directive is inside the parent scope, where $scope.pokemon does not exist/contains no data, which is why the ng-repeat in copy generated nothing.
I cannot put the data inside the parent controller. In my actual application, the system uses a modular design. Each inner controller represents a module which pulls its own set of data from the server. There are multiple web pages (represented by the outer controller) which have a many-to-many relationship with the modules, and the composition of modules in each web page needs to be modifiable. That means the data used by a module must be contained within itself.
How can I rectify this?
Update 1: Redacted. I posted an example of using $emit and $on but Robert's example should be assumed as correct, since I'm still very new to this. Refer to his answer.
Update 2: While testing Alvaro Vazquez's & Robert's solutions, I've identified the specific root cause. When $("#copy").html($("#src").html()); is executed, either the copied ng-repeat executed before any data transfer to outerController occurred, or it was never executed. In the end, modifying what I originally did above makes it fully working:
var app = angular.module("myApp", []);
$(function() {
$("#copy").html($("#src").html());
});
app.controller("innerController", function($scope) {
$scope.pokemon = [{
name: "Bulbasaur",
type: "Grass/Poison"
}, {
name: "Charmander",
type: "Fire"
}, {
name: "Squirtle",
type: "Water"
}];
});
app.controller("outerController", function($scope) {
$scope.pokemon = [{
name: "Bulbasaur",
type: "Grass/Poison"
}, {
name: "Charmander",
type: "Fire"
}, {
name: "Squirtle",
type: "Water"
}];
});
With the location of that particular statement changed, all that is left is to transfer the data to outerController, and at this point both Alvaro's and Robert's solutions work.
As an aside, I think some have advised against using $("#copy").html($("#src").html());. As I have partly described in the comments, the actual application I'm developing consists of multiple web pages, each containing its own outerController. Each innerController is in its own separate HTML file added via an include directive into src. The outerController copies the inner HTML of src, passes it to a third party library, which pastes it into copy and controls its visual layout. $("#copy").html($("#src").html()); is actually part of the third party library's implementation, so I can't change that. Using this statement is therefore a requirement.
I'll post the above as a solution when I get home and has the convenience of a PC keyboard. In the meantime feel free to recommend better solutions to what is found if you have one, thanks!
I think you should make use of angular services.
Declaring a service
First of all, you should declare a service which would 'serve' the data to the rest of your application. For the sake of simplicity, I will only show a method which returns a predefined array, but you could get the data from the server here.
app.service('pokemonService', function () {
return {
getPokemon: function () {
return [{
name: "Bulbasaur",
type: "Grass/Poison"
}, {
name: "Charmander",
type: "Fire"
}, {
name: "Squirtle",
type: "Water"
}];
}
};
});
Using the service in your controller
Then, you can use the service on any of your controllers, injecting it as any other predefined angular service:
app.controller('innerController', function($scope, pokemonService) {
$scope.pokemon = pokemonService.getPokemon();
});
app.controller('outerController', function($scope, pokemonService) {
$scope.outerPokemon = pokemonService.getPokemon();
});
Showing the data in your view
Finally, you can list all your pokémon in any template/part of the template you want:
<!doctype html>
<html ng-app="myApp">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="index.js"></script>
<body>
<div ng-controller="outerController">
<div id="copy">
<!-- Here you can also list the pokémon from your outerController, maybe this time in a list -->
<ul>
<li ng-repeat="poke in pokemonOuter">
{{ poke.name }} - <span class="type">{{ poke.type }}</span>
</li>
</ul>
</div>
<hr>
<div id="src" ng-controller="innerController">
<table>
<th>Name</th>
<th>Type</th>
<tbody>
<tr ng-repeat="poke in pokemon">
<td>{{ poke.name }}</td>
<td>{{ poke.type }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
Wrap up
As you can see, there is no need of messing with the DOM at all. If you use AngularJS, you should do things the Angular way, and working directly with the DOM is not the Angular way at all. Instead, you should put all your data and business logic into services, then use those services in your controllers to retrieve that data and pass it to the view.
Scopes in Angular uses prototypal inheritance, so the child scope will have access to the parent properties but the parent will not have access to the child controller scope properties.
You can use a service to share data or use $emit to send events upwards (upwards until the root scope).
I created a plnkr for you to show you how to use emit (you can find it here)
var app = angular.module("myApp", []);
app.controller("outerController", ['$scope', function($scope) {
console.log('aici');
$scope.$on('pokemonEvent', function(event, mass) { console.log(mass); });
}]);
app.controller("innerController", ['$scope', function($scope) {
$scope.pokemon = [{
name: "Bulbasaur",
type: "Grass/Poison"
}, {
name: "Charmander",
type: "Fire"
}, {
name: "Squirtle",
type: "Water"
}];
$scope.$emit('pokemonEvent', $scope.pokemon);
}]);

asp.net mvc 6 web api + angular resource post array as object property

I am trying to post a complex object from angular to my webapi
the obeject looks like this
{
Name: "test",
Tax: 23,
Addresses: [ {
country: "ro",
city: "bucharest"
},
{
country: "fr",
city "paris"
}]}
and the object from server
public class Model {
public Model (){
Addresses = new List<Address>();
}
public string Name {get; set;}
public int Tax {get; set;}
public List<Address> Addresses {get; set;}
}
and Address has 2 string properties
my old application was written in MVC5 webapi2 and using angularjs $http service
and the object was mapping perfectly, now I changed to MVC6 (asp.net 5) and
if I remove the array from my javascript object it's working perfecyly but I don't have the array, if I add it than the object on server is NULL.
My question is: How can I send an array as object property from angularjs using $resource service to mvc6 controller?
Finally I made it work, my example below:
test1.html
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.28/angular.min.js"></script>
<script type="text/javascript" src="test1.js"></script>
<div ng-controller="TestController">
<div>Test</div>
</div>
</body>
</html>
test1.js
var myApp = angular.module('myApp', []);
myApp.controller('TestController', ['$scope', '$http', function ($scope, $http) {
var dataObj = {
Name: "test",
Tax: 23,
Addresses: [{
country: "ro",
city: "bucharest"
},
{
country: "fr",
city: "paris"
}]
}
$http({
url: 'http://localhost:1522/api/values',
method: 'POST',
data: JSON.stringify(dataObj),
dataType: 'json'
}).success(function (data) {
debugger;
alert("OK");
//TODO.....
}).error(function (data) {
//TODO....
});
}]);
On the screenshot you can see my solution folders and breakpoint after POST:
Replace URLs with your domain and navigate to http://yourdomain/test.html via browser for run your angular app.
P.S.: It example works when your angular app and web api in the same domain. If you want separate them (move angular app to another domain or project) you should config CORS - How do you enable cross-origin requests (CORS) in ASP.NET 5 & MVC 6.

Accessing array of objects in an object using Angular

I am trying to have an array of 30 recipes shown on my view with data from an API call.
// app.js
angular.module('recipeApp', [])
.controller('RecipesCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.mainview = [];
$http.get('/API')
.then(function(response) {
$scope.mainview = response.data;
});
// index.html
<html lang="en" ng-app="recipeApp">
<body ng-controller="RecipesCtrl">
{{mainview}} //this outputs the same data shown on the API call.
// when I try 'ng-repeat' nothing shows up at all
// data from API call (this is just a sample of the data. there is really an array of 30 objects in "recipes")
{
"count": 30,
"recipes": [
{
"publisher": "Closet Cooking",
"f2f_url": "http://food2fork.com/view/35382",
"title": "Jalapeno Popper Grilled Cheese Sandwich",
"source_url": "http://www.closetcooking.com/2011/04/jalapeno-popper-grilled-cheese-sandwich.html",
"recipe_id": "35382",
"image_url": "http://static.food2fork.com/Jalapeno2BPopper2BGrilled2BCheese2BSandwich2B12B500fd186186.jpg",
"social_rank": 100,
"publisher_url": "http://closetcooking.com"
},
{
"publisher": "The Pioneer Woman",
"f2f_url": "http://food2fork.com/view/47024",
"title": "Perfect Iced Coffee",
"source_url": "http://thepioneerwoman.com/cooking/2011/06/perfect-iced-coffee/",
"recipe_id": "47024",
"image_url": "http://static.food2fork.com/icedcoffee5766.jpg",
"social_rank": 100,
"publisher_url": "http://thepioneerwoman.com"
},
When I have {{mainview}} in the html, it shows the same as above, but how can I have it so all 30 recipes are looped in the view? I looked into ng-repeat, but I am very new to Angular and couldn't figure it out. Any information on this would be appreciated.
you can use ng-repeat like this:
<body ng-controller="RecipesCtrl">
<ul>
<li ng-repeat="recipe in mainview.recipes">{{recipe}}</li>
</ul>
</body>
It will generate a li element for every recipe in your array. You can access the properties of a recipe using . as you would in javascript.
{{recipe.publisher}}
Note: ng-repeat works with any elements, I used ul and li for show purposes only.
Something like this may help you:
<ul>
<li ng-repeat="recipe in mainview.recipes">
{{recipe.title}}
<br />
- {{recipe.publisher}}
</li>
</ul>
I think you're looking for a view fragment like:
<div data-ng-repeat="item in mainview.recipes">
<div>
<label>Publisher</label><span>{{item.publisher}}</span>
<div>
<div>
<label>Title</label><span>{{item.title}}</span>
<div>
...
</div>
where ... is whatever else you want to display in the view. Documentation at: https://docs.angularjs.org/api/ng/directive/ngRepeat (though I know you've read it (: )

Angular.js data accessor

I'm trying to learn Angular, and I'm stuck on the following.
I have a PHP background, mostly use Laravel, and in Laravel you can use accessors in your models. So if you have a model User, which has a firstname and lastname. You can create an accessor for fullname which will return both the firstname and lastname:
public function getFullNameAttribute()
{
return $this->firstname . ' ' . $this->lastname;
}
Now, I would like to do the same thing in Angular.
I have a UserController with a user:
function UserController($scope) {
$scope.users = [
{ firstname: 'John', lastname: 'Doe' }
];
}
In the "view" I want to loop over the users with ng-repeat, but instead of doing
{{ user.firstname }} {{ user.lastname }}
I would like to be able to just use
{{ user.fullname }}
Maybe it's called differently in Angular or something, but I just can't seem to find it...
Thanks for the help!
Angular does not yet natively provide model abstractions (beside simplistic $resource service which deals mostly with RESTfull communication layer).
But that doesn't mean you can't write and re-use your own custom model accessors.
Option 1 - Use custom filter
PLUNKER
app.filter("fullName", function () {
return function (user){
return user && [user.firstName, user.lastName].join(' ');
}
});
app.controller("MainCtrl", [
"$scope",
function($scope) {
$scope.user = {
firstName: 'John',
lastName: 'Doe'
};
}
]);
User fullName: {{user | fullName}}
 
Option 2 - Use custom $resource instance accessors:
PLUNKER
app.factory('UserService', function ($resource){
// User is an instance of $resource service which
// in this example uses mock server endpoint
var User = $resource('user.json');
// Custom User accessor method
User.prototype.fullName = function(){
return [this.firstName, this.lastName].join(' ');
}
return User;
});
app.controller("MainCtrl", [
"$scope",
"UserService",
function($scope, UserService) {
$scope.user = UserService.get()
}
]);
User fullName: {{user.fullName()}}
In addition to Stewie's very sophisticated techniques, you can also use ng-init.
<div ng-repeat="user in users"
ng-init="user.fullname = user.firstname + ' ' + user.lastname">
{{user.fullname}}
</div>
http://plnkr.co/zU6vM5f8pI3Veh7jr1R9
angular does not have a model framework, here your scope is the model and any javascript object (plain old javascript object) attached to it. You might be interested in this article which talks about implementing an OO model layer on angular js. https://medium.com/opinionated-angularjs/2e6a067c73bc. Like others have said, implementing filters or directives might be the easiest and is also reusable.

AngularJS - Change the scope of a directive

I'm trying to set up an Angular Directive so that I can re-use a piece of HTML. I have managed to achieve this, but the problem I am facing is when I want to pass some value into that templated HTML, from the containing HTML.
For simplicity's sake, I will use an example of a customer that has multiple addresses (in this context, the customer is an object and each address instance is another object attached to the customer).
Example of the data:
var customer = {
forename: "John",
surname: "Smith",
address1: {
street: "123 Street",
town: "Georgeville",
},
address2: {
street: "67 Maple Grove",
town: "SomeTown"
}
};
Here is an example of my directive setup:
var module = angular.module(...);
module.directive("address", function () {
return {
restrict: 'A',
templateUrl: '/AddressView.html'
};
});
And my attempted usage:
<div ng-model="customer">
<div address></div>
<div address></div>
</div>
And this is what I would like to do to be able to pass the customer's addresses across to the templated HTML:
<div ng-model="customer">
<div address ng-model="customer.address1"></div>
<div address ng-model="customer.address2"></div>
</div>
I may have misunderstood the purpose of directives, or this may not be possible, but if anyone has any suggestions they would be greatly appreciated.
Let me know if you need me to add any further information.
Edit
Here is a Plunker that I have set up to try to illustrate what I am trying to achieve.
you need to isolate the scope for your directive so that things don't get confused for angular.
And your object is better structure this way:
var customer = {
forename: "John",
surname: "Smith",
addresses: [
address1: {
street: "123 Street",
town: "Georgeville",
},
address2: {
street: "67 Maple Grove",
town: "SomeTown"
}
]
};
this way you can do this:
<div class="customer">
<div address ng-repeat="address in customer.addresses">
{{address.town}} {{address.street}}
</div>
</div>
I don't know why you'r using ng-model here?!
This is an example, but if you provide a plunker with an example code helping you will be easier.
Update:
First your ng-controller was in the wrong place you need to move it up so the address directive could be in the scope of the controller so it could access the address object.
Second you have 2 undefined variables: street and town.
And you need to isolate the scope so each directive don't mess with the variables of the other one.
Here's a working plunker.
Hope this help.

Categories