AngularJS not processing JSON correctly - javascript

I have a Play framework project using AngularJS for its views. The controller responsible for querying data makes 2 requests, each returning a JSON block. The first one works correctly and it's displayed properly. The second one's data is pretty much destroyed by Angular (example below).
The JSONs are created correctly prior to being rendered, as it's shown through the application's log.
This is the correct JSON (taken from the Play Framework routed method's log):
{"id":5,"name":"auditoria","url":null,"version":1,"methods":[]}
This is how AngularJS prints it. It tokenizes :
[{},{"0":"a","1":"u","2":"d","3":"i","4":"t","5":"o","6":"r","7":"i","8":"a"},{},{},{"length":0}]
And here's the controller:
app.controller("ViewCtrl", [ "$scope", "$resource", "$routeParams", "apiUrl",
function($scope, $resource, $routeParams, apiUrl) {
var ServiceList = $resource(apiUrl + "/services");
$scope.services = ServiceList.query(); //JSON is displayed properly
if ($routeParams.id) {
jsonServicoQuery = apiUrl + "/services/" + $routeParams.id
var Service = $resource(jsonServicoQuery);
$scope.currentService = Service.query(); //JSON is butchered
}
} ]);
Here's the HTML:
<div class="row">
<div class="col-md-3">
<div class="bs-sidebar hidden-print" role="complementary">
<ul class="nav bs-sidenav">
<li><i class="fa fa-plus"></i></li>
<li ng-repeat="s in services| orderBy:'name'">{{s.nome}}
<ul>
<li ng-repeat="m in s.methods| orderBy:'name'">{{m.name}}
[{{m.id}}]</li>
</ul></li>
</ul>
</div>
</div>
<div class="col-md-9" role="main">
<div class="bs-docs-section">
<div class="page-header">
<!-- displaying the whole currentService JSON for debugging purposes -->
{{currentService}}
</div>
</div>
</div>
</div>
Anybody has any clues about what am I doing wrong?
Update: Service.query() executes the method routed by Play Framework.
Route configuration:
GET /api/services/:id controllers.Services.show(id: String)
And controllers.Services.show(id: String) implementation:
public static Result show(String id) {
Service s = Service.findById(id);
//JSON displayed here is correct, log below
Logger.info("At Services show, json " + Json.toJson(s));
return ok(Json.toJson(s));
}
Log:
[info] application - At Services show, json {"id":5,"name":"auditoria","url":null,"version":1,"methods":[]}

I managed to find the problem, I should have used $scope.currentService = Service.get(); instead of $scope.currentService = Service.query();

Related

Dynamic HTML partial based on REST response

I have an application I am building using an Angular JS front end and a REST-based API back end feeding a MySQL database. There are REST calls made from the front end to the back end to populate or retrieve data in the database. I want to add a drop down selection box to my angular JS front end home page. I want the selection to trigger a REST call to the database, to retrieve a specific value and have that value become a part of a dynamically loaded html partial.
As an example, the drop down would select a model of a car (Toyota Corolla, Honda Accord, etc.) When you select that model, the controller would make a REST call to the appropriate table(s) to get the rest of the information for that car (MPG, size, weight, etc.) Once it did this, it would load a partial HTML on the page that was a template HTML file but with dynamic content. So the page loaded would be /#/carInfo?toyotaCorolla. The template partial html file would load and then the tables on the template would populate with the response from that REST call. So I would essentially have a single template for that page, but it would call a new VERSION of the page based on what was selected.
I am thinking about this in my head and I do not have my application code with me. This question is not for the actual code solution, but for someone to either write up some pseudo code or point me to a demo/example online that is similar to this...if it is even possible. I am doing searches on my own, but I may be searching for the wrong terminology to get this accomplished. Any pointers or help on this would be appreciated.
UPDATE:
Now that I am home, here is a snippet of the code I am having issues with.
<ul class="nav navbar-nav">
<li></li>
<li class="dropdown">
<a href="javascript:void(0)" data-target="#" class="dropdown-toggle" data-toggle="dropdown">
Select a car...
<b class="caret"></b></a>
<ul class="dropdown-menu">
<li ng-model="selectedCar.value" ng-repeat="x.car for x in cars"
ng-change="selectedCarChanged()"></li>
</ul>
</li>
</ul>
That is not populating correctly. I have the same ng code for a <select> implementation using ng-options instead of ng-repeat. I was hoping it would be a simple transition, but the CSS version using the lists is not working.
Please find the code snippet below. Hope this will be helpful
car-list.html
<div ng-controller="carListController">
<select ng-model="selectedCar" ng-change="onSelectCar(selectedCar)">
<option ng-repeat="car in cars">{{car}}</option>
</select>
</div>
carListController.js
app.controller('carListController', function($scope, $location) {
$scope.carList = ['Honda', 'Toyota', 'Suzuki', 'Hyundai'];
$scope.onSelectCar = function(car) {
$location.path('#/carInfo').search({carInfo: car});
}
});
carInfo.html
<div class="carDetails">
<span>Car Name: {{car.name}}</span>
<span>Car Model: {{car.model}}</span>
<span>Car Year: {{car.year}}</span>
<span>Car Size: {{car.size}}</span>
</div>
carInfoDetailsController.js
app.controller('carInfoController', function($scope, $location, $http) {
$scope.car = {};
$scope.init= function() {
$http.get('url/' + $location.search('carInfo'), function(response) {
$scope.car = response;
});
};
$scope.init();
});
appConfig.js
app.config(function($routeProvider){
$routeProvider.when('/carInfo'{
templateUrl: "carInfo.html",
controller: "carInfoController"
});
});
something like:
//in a service
(function() {
function MyService($http) {
var myService = {};
MyService.accessMultiTool = function(){
var args = Array.from(arguments);
var method, url, authorization;
args.forEach(function(item){
if('method' in item){
method = item.method;
}else if ('url' in item){
url = item.url;
}else if ('authorization' in item){
authorization = item.authorization;
}
});
delete $http.defaults.headers.common['X-Requested-With'];
return $http({
method: method,
origin: 'http://someclient/',
url: url,
headers: {'Authorization': authorization}
}).error(function(status){generate some error msg});
};
return MyService;
}
angular
.module('myApp')
.factory('MyService', ['$http', MyService]);
})();
//in a controller
(function () {
function MyCtrl(MyService) {
var myController = this;
this.car_model_options = ["Honda", "Chevy", "Ford", "Nissan"];
this.bound_car_model_obj = {
model: null
};
this.getCarModel = function(){
MyService.accessMultiTool({method: 'GET'}, {url: 'http://somebackend/api/cars/' + myController.bound_car_model_obj.model}, {authorization: this.activeMember.auth}).then(function(data){
myController.setCurrCarModel(data);
});
this.setCurrCarModel = function(data){
myController.currently_selected_car_model = data;
};
};
};
angular
.module('myApp')
.controller('MyCtrl', ['MyService', MyCtrl]);
})();
//in a template
<div ng-controller="MyCtrl as mycontroller">
<select data-ng-init="this.bound_car_model_obj.model = mycontroller.car_model_options[0]" data-ng-model="this.bound_car_model_obj.model" data-ng-options="option for option in mycontroller.car_model_options" >
</select>
<table>
<tr ng-repeat="car in mycontroller.currently_selected_car_model>
<td>{{car.someproperty}}>/td>
<td>{{car.someotherproperty}}>/td>
</tr>
</table>
</div>

How to display a returned json in angular view?

I am implementing a search in the github repository.
I need to display the information that i get from here: https://api.github.com/search/repositories?q=bootstrap . for instance into a view or HTML
<div ng-app="newsearchApp">
<div ng-controller="MainCtrl">
<form action="#/about" method="get">
<input ng-model="searchText" />
<button ng-click="search()">Search</button>
</form>
</div>
</div>
the code for searching the Github repository;
angular.module('newsearchApp')
.controller("MainCtrl", ["$scope", function($scope) {
$scope.searchText = "";
$scope.search = function() {
console.log($scope.searchText);
var item = $scope.searchText;
// console.log(item)
var GithubSearcher = require('github-search-api');
var github = new GithubSearcher({username: 'test#something.com', password: 'passwordHere'});
var params = {
'term': $scope.searchText
};
//i am not certain about the 'userData'
github.searchRepos(params, function(data) {
console.log(data);
$scope.userData = data; //i am not certain about the 'repoData'
});
} }]);
the problem is here, when populating the json object to HTML
<div ng-repeat="repo in userData | filter:searchText | orderBy:predicate:reverse" class="list-group-item ">
<div class="row">
<div class="col-md-8">
<h4>
<small>
<span ng-if="repo.fork" class="octicon octicon-repo-forked"></span>
<span ng-if="!repo.fork" class="octicon octicon-repo"></span>
<small>{{repo.forks_count}}</small>
</small>
<a href="{{repo.html_url}}" target="_blank" >
{{repo.name}}
</a>
<small>{{repo.description}}</small>
<small>{{repo.stargazers_count}}</small>
<a href="{{repo.open_issues_count}}" target="_blank" >
Open Issues
</a>
<small>{{}}</small>
</h4>
</div>
</div>
</div>
the results are null on the HTML but are not null on the console.
thanks in advance
the results are null
The problem is, that Angular doesn't notice that the GitHub server has answered and doesn't update the view. You have to tell Angular manually to re-render the view. Try calling $scope.$apply():
github.searchRepos(params, function(data) {
console.log(data);
$scope.userData = data;
$scope.$apply();
});
If you'd make your request to the GitHub API with Angulars $http service, then this would not be needed - you'll only need $scope.$apply() if something asynchronous happens which doesnt live in the "Angular world" - for example things like setTimeout, jQuery ajax calls, and so on. That's why there are Angular wrappers like $timeout and $http.
More details: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
The GitHub API can be accessed using the AngularJS $http service:
app.controller("myVm", function($scope,$http) {
var vm = $scope;
var url = "https://api.github.com/search/repositories?q=bootstrap"
$http.get(url).then(function onSuccess(response) {
vm.data = response.data;
console.log(vm.data);
})
})
HTML
<div ng-app="myApp" ng-controller="myVm">
<div ng-repeat="item in data.items">
{{item.full_name}}
</div>
</div>
The DEMO on JSFiddle
Since you're not using the Angular $http service, angular is not aware of the changes. You need to manually tell Angular to re-render and evaluate by using
$scope.$apply();

Updating JSON data with AngularJS

I'm rather new to web development and I am trying to get a better understanding of servers and databases and what the limitations are of client-side development.
Right now, I'm learning AngularJS and I have been able to create simple CRUD applications such as a to-do list or online store. Currently, I have always been creating the data for my web applications through regular JavaScript Arrays/Objects.. but now I want to be able to permanently edit/change this data through my own CMS user interface.
Some research has led me to use JSON and the angular $http service to request JSON data from the server.
Now, I am trying to update this JSON data Asynchronously with angularJS and I'm not sure how to do this (see below for my attempt).
Simple To-Do List Application
<body ng-controller="ToDoCtrl">
<div class="container">
<div class="page-header">
<h1>
{{todo.user}}'s To Do List
<span class="label label-default" ng-hide="incompleteCount() == 0"
ng-class="warningLevel()">
{{ incompleteCount() }}
</span>
</h1>
</div>
<div class="panel">
<div class="input-group">
<input class="form-control" ng-model="actionText">
<span class="input-group-btn">
<button class="btn btn-success" ng-click="addItem(actionText, todo.items)">Add</button>
</span>
</div><!-- end input-group -->
<table class="table table-striped">
<thead>
<tr>
<th>Descriptions</th>
<th>Done</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in todo.items | checkedItems: showComplete | orderBy: 'action'">
<td>{{item.action}}</td>
<td><input type="checkbox" ng-model="item.done"></td>
<td><button ng-click="deleteItem(item, todo.items)" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<div class="checkbox-inline">
<label><input type="checkbox" ng-model="showComplete">Show Complete</label>
</div>
<div class="input-group">
<button ng-click="save()" class="btn btn-primary">Save Changes</button>
<p>{{msg}}</p>
</div>
</div><!-- end panel -->
</div>
<!-- Vendor JS -->
<!-- Angular -->
<script src="vendors/angular.min.js"></script>
<!-- Modules -->
<script src="app.js"></script>
</body>
app.js
var model = {
user: "Alex"
};
angular.module('todoApp', [])
.run(function($http) {
$http.get("todo.json").success(function(data) {
model.items = data;
});
})
.controller('ToDoCtrl', ['$scope', '$http', function($scope, $http) {
$scope.todo = model;
$scope.incompleteCount = function() {
var count = 0;
angular.forEach($scope.todo.items, function(item) {
if (!item.done) {
count++
}
});
return count;
};
$scope.warningLevel = function() {
return $scope.incompleteCount() < 3 ? "label-success" : "label-warning";
};
$scope.addItem = function(actionText, sourceArray) {
sourceArray.push(
{
action: actionText,
done: false,
}
);
$scope.actionText = '';
};
$scope.deleteItem = function(item, sourceArray) {
for(var i = 0, j = sourceArray.length; i < j; i++) {
if(item.action == sourceArray[i].action) {
sourceArray.splice(i, 1);
return;
}
}
};
$scope.save = function() {
$http.post('C:\Users\Alex\Desktop\Development\"Web Design"\2015\todoApp\public\src\todo.json', $scope.todo.items).then(function(data) {
$scope.msg = 'Data saved '+ JSON.stringify($scope.todo.items);
});
};
}])
.filter("checkedItems", function() {
return function(items, showComplete) {
var resultArr = [];
angular.forEach(items, function(item) {
if(item.done == false || showComplete == true) {
resultArr.push(item);
}
});
return resultArr;
}
});
I used this Post for the $scope.save function, but I receive an error: "XMLHttpRequest cannot load. Cross origin requests are only supported for protocol schemes: http, data, chrome..."
$scope.save = function() {
$http.post('C:\Users\Alex\Desktop\Development\"Web Design"\2015\todoApp\public\src\todo.json', $scope.todo.items).then(function(data) {
$scope.msg = 'Data saved '+ JSON.stringify($scope.todo.items);
});
};
Basically, I just want to update my todo.json file with the current contents of my $scope.todo.items array. I think the simplest way would be to delete the current contents of the JSON data and replace with current contents of $scope.todo.items, but I don't know much about this stuff.
Thanks for any help.
Let's start with some concepts first:
1.- a JSON file is just a text file, it can be a product of a database query or it can be generated dynamically by a server, but at the end of the day is just a text file.
2.- the $http service deals with request to HTTP servers, like the Apache Web Server, or NodeJS Http Server, running your software with backend technology, there's a multitude of servers and some can run in your machine as well as remotely.
3.- GET and POST are HTTP methods, that must be made to a server running your backend. The most common one, the GET method is usually used to get data from a server, like text files, or JSON files.
4.- In a file server like the one Windows provides you for local development, the GET method can bring up files from your file system (like "todo.json"). This file server is really basic, it just accepts GET requests and that's all it should accept.
5.- In your backend software, you define an Endpoint, that should be an address where your backend is ready to receive a POST request, and you also need to define what does this POST request do.
It's a long step between going from your angular file to defining an endpoint in a server, you will go across different technologies, the Angular framework is not a backend technology, it's a frontend library.
If you want to get into these concepts, a TODO List project is a great first project, sites like http://www.todobackend.com/ can show you all sort of TODO projects in a myriad of different backends and frontends.

Ng-repeat is not displaying json data

thanks advance for any support. So I have a factory that uses a post to get some data from a C# method. That all seems to be working as I can see the data in the console log when it gets returned. However, when I get the data, I can't seem to get it to display properly using ng-repeat.
I've tried a couple different ways of nesting ng-repeats and still no luck. So now I'm thinking I may have not passed the data from the call properly or my scope is off. I've also tried passing data.d to hangar.ships instead of just data. Still pretty new to angular so in any help to point me int he right direction is greatly appreciated.
app code:
var app = angular.module('shipSelection', ['ngRoute', 'ngResource']);
app.controller('ShipController', function ($scope, ShipService) {
var hangar = this;
hangar.ships = [];
var handleSuccess = function (data, status) {
hangar.ships = data;
console.log(hangar.ships);
};
ShipService.getShips().success(handleSuccess);
});
app.factory('ShipService', function ($http) {
return {
getShips: function () {
return $http({
url: '/ceresdynamics/loadout.aspx/getships',
method: "post",
data: {},
headers: { 'content-type': 'application/json' }
});
}
};
});
Markup:
<div class ="col-lg-12" ng-controller="ShipController as hangar" >
<div class =" row">
<div class="col-lg-4" ><input ng-model="query" type="text"placeholder="Filter by" autofocus> </div>
</div><br />
<div class="row">
<div ng-repeat="ship in hangar.ships | filter:query | orderBy:'name'">
<div class="col-lg-4">
<div class="panel panel-default">
<div>
<ul class="list-group">
<li class="list-group-item" >
<p><strong>ID:</strong> {{ ship.ShipID }} <strong>NAME:</strong> {{ ship.Name }}</p>
<img ng-src="{{ship.ImageFileName}}" width="100%" />
</li>
</ul>
</div>
</div><!--panel-->
</div> <!--ng-repeat-->
</div>
</div>
</div> <!--ng-controller-->
JSON returned from the post(From the console.log(hangar.ships):
Object
d: "[{"ShipID":"RDJ4312","Name":"Relentless","ImageFileName":"Ship2.png"},{"ShipID":"ZLH7754","Name":"Hercules","ImageFileName":"Ship3.png"},{"ShipID":"FER9423","Name":"Illiad","ImageFileName":"Ship4.png"}]"
__proto__: Object
As per AngularJS version 1.2, arrays are not unwrapped anymore (by default) from a Promise (see migration notes). I've seen it working still with Objects, but according to the documentation you should not rely on that either.
Please see this answer Angular.js not displaying array of objects retrieved from $http.get
What happens if you add JSON.parse(data);
If this works you should add some checks in and perhaps migrate that logic to the service. Or use $resource per the other answer.
https://github.com/angular/angular.js/commit/fa6e411da26824a5bae55f37ce7dbb859653276d

AngularJS: Scope in multiple views not updating

I have one controller and two views.
ClustersContorller
angular.module('app.controllers').controller('ClustersController', [
'$scope', 'ClustersService', function($scope, ClustersService) {
ClustersService.getAll().success(function(data) {
$scope.clusters = data;
});
$scope.$on('cluster:added', function(event, data) {
ClustersService.createNew(data).then(
function(res) {
$scope.clusters.push(res.data);
},
function(res) {
console.log( 'Unable to create a cluster!' );
}
);
});
}
]);
Now one view is working great when I send the HTTP request and update the scope variable by pushing to $scope.clusters:
<section class="clusters">
<h2 ng-show="clusters.length < 1">You have no clusters :(</h2>
<a class="btn btn-default btn-block" data-ng-repeat="cluster in clusters" data-template="{{cluster.templateId}}">
<h2> {{ cluster.name }} </h2>
<p> {{ cluster.description }} </p>
</a>
<add-cluster-modal></add-cluster-modal>
</section>
But the other view that is bound with this controller does not update scope.clusters in the bindings:
<ul class="dropdown-menu" role="menu" data-ng-controller="ClustersController">
<li data-ng-repeat="cluster in clusters">
<a> {{cluster.name}} </a>
</li>
</ul>
Just to be clear the first view is bound by the $routeProvider and the second one is a part of a template included directly into the app main html file by ng-include=" 'templates/partials/header.html' "
Please feel free to ask me if something is confusing...
Angular controllers are not singletons, and every time you use ng-controller in a view, you create a new instance of that controller (see documentation). This is the reason why your second controller doesn't show the data - it's scope is not aware of the scopes of other instances.
You can either save the model's data under $rootScope, or create some eventing mechanism in your controller, that would inform other instances of data change.

Categories

Resources