Angular template doesn't render data from controller - angularjs

I'm setting up an Angular JS app to consume a Django REST API, and I'm stuck on the rendering of data.
I already asked another question (this), but the solution that I've been given doesn't work.
I was wondering that maybe there is something wrong in the API view, could it be a problem when trying to render the data from the Angular controller?
Anyway, this is my Angular app + template (edited as suggested in the other stackoverflow question)
base.html
<body ng-app="schoolApp" ng-controller="schoolCtrl as vm">
<p>Hello {{vm.name}}!</p>
<div>
<table class="table table-striped">
<thead>
<tr>
<th>Classroom</th>
<th>School</th>
<th>Floor</th>
<th>Academic year</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="classroom in vm.classrooms">
<td>{{classroom.classroom}}</td>
<td>{{classroom.school.school_name}}</td>
<td>{{classroom.floor}}</td>
<td>{{classroom.academic_year}}</td>
</tr>
</tbody>
</table>
</div>
</body>
app.js
var schoolApp = angular.module('schoolApp', ['ngResource']);
schoolApp.factory('Classroom', ['$resource', function($resource) {
return $resource('/classrooms/?format=json', {}, {
query: {
method: 'GET',
isArray: true,
}
});
}]);
schoolApp.controller('schoolCtrl', function($scope, Classroom) {
var vm = this;
vm.name = 'World';
Classroom.query().$promise.then(function(data) {
console.log('Success: '+JSON.stringify(data));
vm.classrooms = data;
}, function (reason) {
console.log('ERROR: '+JSON.stringify(reason));
});
});
I was thinking that maybe there is some problem on the view, so here's the REST API view
class HomePageView(TemplateView):
template_name = 'school_app/base.html'
class StudentViewSet(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
class ClassroomViewSet(viewsets.ModelViewSet):
queryset = Classroom.objects.all()
serializer_class = ClassroomSerializer
What am I doing wrong?
UPDATE:
This is what I get
Data on the console: yes.
Data on the tables: no.

As you are using controllerAs syntax, you have binded all data to controller context. So you could get data on view using its alias like vm.classrooms
<tr ng-repeat="classroom in vm.classrooms">

Have you tried to output in the html the content of the variable vm.classrooms.
You can do it with <pre>{{vm.classrooms|json}}</pre>
It seems to me that you are not binding correctly the variables inside the table,
i can't see it clearly in the picture, it can be {{ classroom.school.academic_year }} ??

Related

I am using MVC 4 and angularjs in this actually I want to call an angular js function on page load

i want to invoke angularjs function (DeptSpecific) on page load and am passing ID as the parameter which is hard coded. And that i am passing via ng-init="DeptSpecific('1')". I am learning angularjs please suggest me how to call a function and pass a parameter to it on page load without any click or anything just when a page load a function should be called. If you are thinking why i used ng-init ..there is no specific reason for this it might be wrong but it calls the function well i can see that in debugger (f12) but $scope.s i undefined even though there is a matched ID.
$scope.DeptSpecific = function (ID) {
var BelongsToThisDepartment = [];
$http.get('/Department/getDept').success(function (response) {
$scope.departments = $scope.$eval(response);
});
angular.forEach($scope.departments, function (item1) {
if (item1.ID == ID) {
BelongsToThisDepartment.push(item1);
}
})
$scope.s = $scope.$eval(angular.toJson(BelongsToThisDepartment));
// console.log(JSON.stringify($scope.s));
}
<div ng-app="MyApp">
<div ng-controller="MyController">
<table class="tableData" border="0" cellspacing="0" cellpadding="0" ng-init="DeptSpecific('1')">
<thead>
<tr>
<th></th>
<th>ID</th>
<th>NAME</th>
<th>LOCATION</th>
</tr>
</thead>
<tbody ng-repeat="O in s">
<tr ng-class-even="'even'" ng-class-odd="'odd'">
<td class="CX" ng-click="student(O.ID)"><span>+</span></td>
<td>{{O.ID}}</td>
<td>{{O.Name}}</td>
<td>{{O.Location}}</td>
</tr>
Looking at your code, using ng-init is fine, but your $scope.departments may not be accessible outside of the .success method.
angular.module('MyApp')
.controller('MyController', ['$scope', function($scope) {
$scope.DeptSpecific = function (ID) {
var BelongsToThisDepartment = [];
$http.get('/Department/getDept')
.success(function (response) {
$scope.departments = $scope.$eval(response);
angular.forEach($scope.departments, function (item1) {
if (item1.ID == ID) {
BelongsToThisDepartment.push(item1);
}
})
$scope.s = $scope.$eval(angular.toJson(BelongsToThisDepartment));
console.log($scope.s);
});
}
}]);
now if that works for you but you also want to be able to access $scope.s outside of that .success;
You can write a function, add it into .success pass the value returned onSucess, and do what you want to do.
.success(function(response) {
callAFunction(response);
}

$resolved: false in Angular JS response

I'm setting up an Angular JS app that consumes a Django REST API.
I want to show a HTML list of classrooms.
This is my template
<body>
<div ng-app="schoolApp" ng-controller="schoolCtrl">
<table class="table table-striped">
<thead>
<tr>
<th>Classroom</th>
<th>School</th>
<th>Floor</th>
<th>Academic year</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="classroom in classrooms">
<td>{{classroom.classroom}}</td>
<td>{{classroom.school.school_name}}</td>
<td>{{classroom.floor}}</td>
<td>{{classroom.academic_year}}</td>
</tr>
</tbody>
</table>
</div>
</body>
This is the script
var schoolApp = angular.module('schoolApp', ['ngResource']);
schoolApp.factory('Classroom', ['$resource', function($resource) {
return $resource('/classrooms/?format=json', {}, {
query: {
method: 'GET',
isArray: true,
}
});
}]);
schoolApp.controller('schoolCtrl', function($scope, Classroom) {
Classroom.query().$promise.then(function(data) {
var data = Classroom.query({});
$scope.classrooms = data;
console.log(Classroom.query({}));
});
});
The problem is, I think, that I get - I can see it in the console -, $resolved: false.
How can I resolve that?
UPDATE:
Given that I can't resolve the issue, I was wondering that maybe I've set up badly something else, like... the view?
This is the one I got
class HomePageView(TemplateView):
template_name = 'school_app/base.html'
class StudentViewSet(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
class ClassroomViewSet(viewsets.ModelViewSet):
queryset = Classroom.objects.all()
serializer_class = ClassroomSerializer
Maybe I have to add something to HomePageView or setting it up in another way?
UPDATE:
This is what I get on the console with the debugger "on"
Success: [{"school":{"id":1,"school_name":"IPSIA F. Lampertico","address":"Viale Giangiorgio Trissino, 30","city":"Vicenza"},"academic_year":"2015/2016","classroom":"1^A","floor":0,"students":[{"classroom":1,"first_name":"Stefano","last_name":"Rossi","gender":"M","birthday":"1998-06-22"},{"classroom":1,"first_name":"Luca","last_name":"Possanzini","gender":"M","birthday":"1999-11-22"}]},{"school":{"id":2,"school_name":"ITIS A. Rossi","address":"Via Legione Gallieno, 52","city":"Vicenza"},"academic_year":"2015/2016","classroom":"2^B","floor":0,"students":[{"classroom":2,"first_name":"Sergio","last_name":"Lazzari","gender":"M","birthday":"2001-01-29"}]},{"school":{"id":3,"school_name":"Liceo Scientifico G.B. Quadri","address":"Viale Giosuè Carducci, 17","city":"Vicenza"},"academic_year":"2015/2016","classroom":"3^C","floor":0,"students":[{"classroom":3,"first_name":"Lucia","last_name":"Modella","gender":"F","birthday":"2000-05-22"}]},{"school":{"id":4,"school_name":"Istituto Professionale Statale B.Montagna","address":"Via Mora, 93","city":"Vicenza"},"academic_year":"2015/2016","classroom":"4^D","floor":1,"students":[{"classroom":4,"first_name":"Mirko","last_name":"Van Der Sella","gender":"M","birthday":"2002-12-25"}]}]
Practically, the whole Json response.
When you call query of $resource, it returns a reference to an object or array with $resolved = false, until the REST API calls finishes and populates your object. So, $resolved = false is probably correct and indicates that you have not receive the data yet.
Here is a working plunker based on your code.
The controller is:
app.controller('schoolCtrl', function($scope, Classroom) {
var vm = this;
vm.name = 'World';
Classroom.query().$promise.then(function(data) {
console.log('Success: '+JSON.stringify(data));
vm.classrooms = data;
}, function (reason) {
console.log('ERROR: '+JSON.stringify(reason));
});
});
This is what I do for debugging REST web API... once the call works, you can switch to a lighter version:
app.controller('schoolCtrl', function($scope, Classroom) {
var vm = this;
vm.name = 'World';
vm.classrooms = Classroom.query();
});
I created a classroom JSON (guessing your format):
[
{"classroom":"0", "school": {"school_name":"anc"} },
{"classroom":"1", "school": {"school_name":"Sorbonee"} }
]
And the HTML:
<body ng-controller="schoolCtrl as vm">
<p>Hello {{vm.name}}!</p>
<div>
<table class="table table-striped">
<thead>
<tr>
<th>Classroom</th>
<th>School</th>
<th>Floor</th>
<th>Academic year</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="classroom in vm.classrooms">
<td>{{classroom.classroom}}</td>
<td>{{classroom.school.school_name}}</td>
<td>{{classroom.floor}}</td>
<td>{{classroom.academic_year}}</td>
</tr>
</tbody>
</table>
</div>
</body>
I changed the URL in the factory to make it work on plnkr, but the rest is identical:
app.factory('Classroom', ['$resource', function($resource) {
return $resource('classrooms?format=json', {}, {
query: {
method: 'GET',
isArray: true,
}
});
}]);
Please note that I use var vm=this and ControllerAs syntax to avoid any scope issues based on this article.
On ngResource from the doc: "It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods."
Let us know if this helps.

How get my controller to make GET request when tab is selected?

I am pretty new to angular and I am working on a data entry web page. The web page has three tabs. Vendor, Products and Types. I started working on the Types tab first. I'd be happy if I could just display the results of a GET request to my Rest API. My Rest API works:
# curl http://192.168.1.115:8080/type
[
{"_id":"56415e7703aba26400fcdb67","type":"Skiing","__v":0},
{"_id":"56417a8503aba26400fcdb68","type":"Bannana","__v":0},
{"_id":"56417a8d03aba26400fcdb69","type":"Carrot","__v":0},
{"_id":"56417a9603aba26400fcdb6a","type":"Beer","__v":0}
]
Here's the pertinent part of my html UPDATED I now have st-safe-src=all_typesbut still no joy ...
<div ng-controller="typeCtrl" class="tab-pane" id="types-v">
<p>The number {{3 + 4}}.</p>
<p>message is {{message}}</p>
<table st-table="displayedCollection" st-safe-src="all_types" class="table table-striped">
<tbody>
<tr ng-repeat="x in displayedCollection">
<td>{{x.type}}</td>
</tr>
</tbody>
</table>
</div> <!-- end Types Tab -->
... and here is my typeCtrl.js ...
app.controller("typeCtrl", function($scope,$http) {
$scope.type_to_look_for = "";
$scope.message = "this is the message. (from typeCtrl)";
$scope.itemsByPage=15;
$scope.all_types = function () {
$http.get("http://192.168.1.115:8080/type").then(function(response) {
console.log(response);
console.log(response.data);
return response.data;
});
}
});
... but when I click on the Types tab my data does not display. I looked developer console and I do not even see the GET request kickoff. And my web page looks like this ...
... what am I doing wrong?
There is nothing that calls all_types. Run http.get and assign the response to all_types
app.controller("typeCtrl", function($scope,$http) {
$scope.type_to_look_for = "";
$scope.message = "this is the message. (from typeCtrl)";
$scope.itemsByPage=15;
$http.get("http://192.168.1.115:8080/type").then(function(response) {
$scope.all_types = response;
});
}
});
My understanding is that you want a get request to be fired whenever you click on the Types tab, right? If so, just use ng-click to call your all_types function as follows:
<div ng-controller="typeCtrl" ng-click="all_types()" class="tab-pane" id="types-v" >
Also, you do not need to return response.data in your controller. Just assign the data to a scope object and use it in the template.
And finally, I would suggest wrapping all your ajax calls in factories and then inject those factories in your controllers.
Here is your code
<div ng-controller="typeCtrl" class="tab-pane" id="types-v">
<p>The number {{3 + 4}}.</p>
<p>message is {{message}}</p>
<table st-table="types" class="table table-striped"><!-- Do not need st-safe-src -->
<tbody>
<tr ng-repeat="x in types"><!-- Use the Collection name as types-->
<td>{{x.type}}</td>
</tr>
</tbody>
</table>
</div>
Controller Code
app.controller('typeCtrl', function($scope, $http) {
$scope.type_to_look_for = "";
$scope.message = "this is the message. (from typeCtrl)";
$scope.itemsByPage=15;
$http.get("http://192.168.1.115:8080/type").then(function(response) {
console.log(response.data);
$scope.types = response.data;
});
});
Here is working the plunker

function not executing on different view

I am new to angular JS. I have created a simple page using ngRoute.
In the first view I have a table, and on clicking it redirects to second view.
But I have called two functions using ng-click the changeView functions is running fine. But the second function fails to execute on the second view.
But Its running fine if using on the first view itself.
Heres the code for first view
Angular.php
<div ng-app="myApp" ng-controller="dataCtr">
<table class='table table-striped'>
<thead>
<tr>
<td>Title</td>
<td>Stream</td>
<td>Price</td>
</tr>
</thead>
<tbody>
<tr ng-repeat = "x in names | filter:filtr | filter:search" ng-click=" disp(x.id);changeView()">
<td >{{ x.Title }}</td>
<td>{{ x.Stream }}</td>
<td>{{ x.Price }}</td>
</tr>
</tbody>
</table>
</div>
heres the second View
details.php:
<div ng-app="myApp" ng-controller="dataCtr">
<div class="container">
SELECTED:<input type="textfield" ng-model="stxt">
</div>
</div>
heres the js file:
var app = angular.module("myApp", ['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/angular', {
templateUrl: 'angular.php',
controller: 'dataCtr'
})
.when('/details', {
templateUrl: 'details.php',
controller: 'dataCtr'
})
.otherwise({
redirectTo: '/angular'
});
});
app.controller('dataCtr', function($scope ,$http ,$location ,$route ,$routeParams) {
$http({
method: "GET",
url: "json.php"})
.success(function (response) {$scope.names = response;});
$scope.changeView = function()
{
$location.url('/details');
};
$scope.disp = function(id)
{
$scope.stxt = $scope.names[id-1].Title;
};
});
The disp function is working fine on the angular view. But not being routed on the second view. I think the syntax for calling the two views in ng click is correct. OR if there any other method to call the associated table cell value to the second view. Please Help.
After a lots of research i figured it out.I used a factory service
app.factory('Scopes', function ($rootScope) {
var mem = {};
return {
store: function (key, value) {
$rootScope.$emit('scope.stored', key);
mem[key] = value;
},
get: function (key) {
return mem[key];
}
};
});
Added this to JS.
Because The scope gets lost on second Controller ,Services help us retain the scope value and use them in different controllers.Stored the scope from first controller
app.controller('dataCtr', function($scope ,$http ,$location,$rootScope,Scopes) {
Scopes.store('dataCtr', $scope);
//code
});
and loaded in the seconded controller.
app.controller('dataCtr2', function($scope ,$timeout,$rootScope,Scopes){
$scope.stxt = Scopes.get('dataCtr').disp;
});
Second view is not working because you cannot use $scope.$apply() method.
$apply() is used to execute an expression in angular from outside of the angular framework.$scope.$apply() right after you have changed the location Angular know that things have changed.
change following code part ,try again
$location.path('/detail');
$scope.$apply();

Reuse components in AngularJS

As a new AngularJS developer (coming from PHP+Laravel world) I'm facing some troubles designing the architecture of my new app.
Which is the best way to implement a CRUD app where entities are used more than once along the app?
For example: we have the entities 'document' and 'project'. Documents can be listed and viewed alone, but also can be attached to projects. Inside the project detail view I would like to include the attached documents, using the same template used when listing the documents alone. This widget should have its own controller and methods, since its need to make some API calls and apply some business logic; and receive the parent project data in some way.
What should I use for document listing? A directive, a ng-include or some other?
You should use module to use it as reusing component.
https://docs.angularjs.org/guide/module
i'm utilizing angular module and factory like this:
app.js
'use strict';
/* App Module */
var app = angular.module('my-app', [
'my-models',
]);
my-models.js
var myModels = angular.module('my-models', []);
myModels.factory('DocumentsModel', function ($http)
{
var DocumentsModel = function ()
{
};
DocumentsModel.get_documents = function (page, results_per_page)
{
var data = {
page: page,
results_per_page: results_per_page
};
var json = angular.toJson(data);
return $http.post('/api/documents', json);
};
DocumentsModel.set_document_state = function (document_id, document_state_id)
{
var json = angular.toJson(
{
'document_state': document_state_id
}
);
return $http.post('api/document/'+document_id', json);
};
return DocumentsModel;
});
using angular dependency injection mechanism, you can re-use this logic in multiple controllers by adding DocumentsModel to the controller function as parameter:
documents-ctrl.js
var app = angular.module('my-app');
var controller = app.controller("DocumentsCtrl",
function ($scope, DocumentsModel)
{
DocumentsModel.get_documents()
.success(function(data){
$scope.documents = data.documents;
});
});
in addition, you cad define one for your 'project' entity.
Edit:
Javier commented:
assuming your documents response is
[{name: ... , size: ... , last_modified: ... }, {name: ... , size: ... , last_modified: ... }, ...]
you can utilize ng-repeat like this:
<table>
<thead>
<tr>
<th>Name</th>
<th>Size</th>
<th>Last Modified</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="document in documents">
<td>{{ document.name }}</td>
<td>{{ document.size/1024 | number:4 }} MB</td>
<td>{{ document.last_modified | date:'yyyy-MM-dd HH:mm:ss' }}</td>
</tr>
</tbody>
</table>
Just add it as a dependency to your own module. Like
angular.module('test', []);
angular.module('test2', ['test']);
You might want to take a look at Yeoman

Resources