ng-repeat not showing table rows after loading - javascript

I am experiencing some problems with not showing any data with ng-repeat.
Flow is as follows
I call a sharedObjects service before requesting data from the server, in case the needed data is already loaded and available.
The loading part works, and shows up in the console. There is a timeout set for testing purposes, and the page stays on loading view until data is loaded. But the rows are not showing up the first time. After going to another page and back to the page in question, the data shows fine and is loaded from the sharedObjects service.
My code is as follows
// dataService aka sharedObjects
angular.module('app').factory('dataService', function(){
var List = undefined;
return {
getList: function () {
return List;
},
setList: function (list) {
List = list;
},
}
});
// controller
angular.module('appWebmenu').controller('appWebmenuController', ['$scope', 'dataService', 'webmenuService', function($scope, dataService, webmenuService){
$scope.listLoaded = false; // data is not loaded flag
$scope.listError = false; // no errors occured yet flag
// function to load and pass the data to the ng-repeat
$scope.getItemList = function(){
// trying for preloaded data
$scope.itemList = dataService.getList();
console.log($scope.itemList);
if($scope.itemList === undefined){
// data not previously loaded get from server (service)
$scope.itemList = webmenuService.getWebmenuList($scope.shop_id,$scope.token)
// after data loads promise returned
.then(
// on success
function(data){
// by adding the extra call to the dataService, it works. Brilliant
$scope.itemList = dataService.getList();
$scope.listLoaded = true; // set the data as loaded
return data;
},
// on failure
function(data){
$scope.listLoaded = false; // data still not loaded
$scope.listError = true; // an error has occured
alert(data.errorMessage);
});
}
else{
$scope.listLoaded = true; // data returned from preloaded dataService
}
return $scope.itemList;
};
// dataRetrieval which connects to the server
angular.module('appWebmenu').factory('webmenuService', ['$q','$http', 'dataService','$timeout', function($q, $http, dataService, $timeout){
return {
getWebmenuList: function (shop_id, token) {
var dfr = $q.defer(); // init promise
// set retrieval settings
var req = {
method: 'POST',
url: "../api/v1/Webmenu/menulist/all",
headers: {
'Content-Type': 'application/json'
},
data: {shop_id: '' + shop_id,token: '' + token }
};
// execute retrieval request
$http(req)
// when loaded
.then(
// on success
function(response){
dataService.setList(response.data); // setting the data to the dataService for reuse
$timeout(function(){
dfr.resolve(response.data);
},5000);
},
// on failure
function(response){
dfr.reject( false );
});
return dfr.promise;
}
};
}]);
// directive
angular.module('appWebmenu').directive('appWebmenuList', function() {
return {
//require: '^appWebmenu',
transclude: true,
templateUrl: 'external/appWebmenu/appWebmenuListTemplate.html',
controller: 'appWebmenuController',
scope: {
},
link: function(scope, el, attr, ctrl) {
}
}
});
// Template
<!-- to show while loading -->
<div ng-if="!listLoaded"> // corresponds with $scope.listLoaded in the controller
<i class="fa fa-spinner fa-spin fa-3x"></i>
</div>
<!--show when an error occures-->
<div ng-if="listError"> // corresponds with $scope.listError in the controller
{{ 'ERROR_LOADING' | translate }}
</div>
<!-- show when data is loaded and no errors occured-->
<div ng-if="listLoaded && !listError && itemList"> // check if list is loaded, no errors occured and if itemList is not empty or undefined
<table class="webmenu-table-main">
<thead>
<tr>
<th>{{'PAGE' | translate}}</th>
<th>{{'ACTIONS' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in itemList">
<script>console.log('menu name is {{ item.menu_name }}');</script>
<td>{{item.menu_name}}</td>
<td>
<i class="fa fa-search" title="{{ 'DETAILS' | translate }}" ng-click="details(item.menu_id)" ng-if="item.menu_is_editable != '0'"></i>
<i class="fa fa-pensil-square" title="{{ 'EDIT' | translate }}" ng-click="edit(item.menu_id)" ng-if="item.menu_is_editable != '0'"></i>
<i class="fa fa-trash-o" title="{{ 'DELETE' | translate }}" ng-click="remove(item.menu_id)" ng-if="item.menu_is_editable != '0'"></i>
<i class="fa fa-plus" title="{{ 'ADDMENU' | translate }}" ng-click="addNewMenuItem(item.menu_id)"></i>
<i class="fa fa-external-link-square" title="{{ 'OPENEDITOR' | translate }}" ng-click="openInEditor(item.menu_id)" ng-if="item.menu_is_editable != '0'"></i>
</td>
</tr>
</tbody>
</table>
</div>
Is there something I am missing? This works only the times after the first time when the data is preloaded.

webmenuService.getWebmenuList returns a promise. Not data. You need to set $scope.itemList inside your then function call.
if($scope.itemList === undefined){
// data not previously loaded get from server (service)
var qListPromise = webmenuService.getWebmenuList($scope.shop_id,$scope.token)
// after data loads promise returned
.then(
// on success
function(data){
$scope.listLoaded = true; // set the data as loaded
return data;
},
// on failure
function(data){
$scope.listLoaded = false; // data still not loaded
$scope.listError = true; // an error has occured
alert(data.errorMessage);
});
qListPromise.then( function(data) { $scope.itemList = data;
}).catch( function(error) { throw error; });

Related

Including AngularJS code in HTML generated in JS

I'm working with an old version of AngularJS (1.3). I've got a page that I want to conditionally show different things based on the value in the database. If the value in the database is changed via user interaction, I want to update what's shown automatically. Part of what I show, however, is HTML and in that HTML I need to include some AngularJS code.
If the value is True, I want to show this HTML:
Your value is True. To set it to False, <a ng-click="myToggleFunction('paramValueFalse')">click here</a>.
If the value is False, I want to show this HTML:
You haven't done the thing, to do the thing, <a ng-click="myDifferentFunction('someOtherParamValue')">click here</a>.
I've got it so close to working: the content that shows changes out depending on what the user's value is, and it updates appropriately, and it's even rendering the HTML correctly (using $sce)... But the ng-click isn't functioning. Can you include angular in HTML that's being injected via JS like that?
Full code:
HTML:
<span ng-bind-html="renderHtml(html_content)"></span>
Controller:
function MyCtrl ($scope, $http, $sce, Notification) {
$scope.username = context.targetUsername;
$scope.content_options = {
'yes' : 'Your value is True. To set it to False, <a ng-click="myToggleFunction(" + "'paramValueFalse'" + ')">click here</a>.',
'no' : 'You haven\'t done the thing, to do the thing, <a ng-click="myDifferentFunction(" + "'someOtherParamValue'" + ')">click here</a>.'
}
$http.get(
'/api/v1/user/' + $scope.username + '/?fields=myBooleanField' // django rest api call
).then(function(response) {
$scope.user = response.data;
if ($scope.user.myBooleanField) {
$scope.html_content = $scope.content_options['yes'];
} else {
$scope.html_content = $scope.content_options['no'];
}
});
});
$scope.myToggleFunction = function(paramValue) {
// toggle value in the db
if (accepted === 'true') {
var success = "You turned on the thing";
var content = "yes";
} else {
var success = "You turned off the thing";
var content = "no";
}
$http({
method: 'GET',
url: '/api/v1/user/' + $scope.username + '/my_boolean_field/?value=' + paramValue, // django rest api call
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function(response) {
$scope.html_content = $scope.content_options[content];
Notification.success(success);
}, function(response) {
Notification.error("There was an error.");
});
};
$scope.myDifferentFunction = function(someOtherParamValue) {
// do some other stuff
};
$scope.renderHtml = function(html_code) {
return $sce.trustAsHtml(html_code);
};
}
MyCtrl.$inject = ['$scope', '$http', '$sce', 'Notification'];
As Sagar said above, the reason this is happening is because the html code returned by renderHtml is not compiled by AngularJS. I tried a few different takes on creating a directive that recompiles angular. For example:
https://github.com/incuna/angular-bind-html-compile
Rendering directives within $sce.trustAsHtml
ng-click doesn't fire when added post load
However, none of these were working for me. I'm not sure why; the content just wasn't displaying but there were no JS errors.
I ended up finding this solution, and it worked for me: Angular: ng-bind-html filters out ng-click?
Essentially, the solution is use raw JS to directly call the Angular functions, rather than using the ng-click directive in the JS-generated HTML content.
Here's what it looks like:
Template:
<div id="angularHtml" ng-bind-html="html_content">
<script>
function callAngular(controllerFunction, functionParam) {
var scope = angular.element(document.getElementById('angularHtml')).scope();
scope.$apply(function() {
{# couldn't figure out how to reference the function from the variable value, so this is hacky #}
if (controllerFunction == "myToggleFunction") {
scope.myToggleFunction(functionParam);
} else if (controllerFunction == 'myDifferentFunction') {
scope.myDifferentFunction(functionParam);
}
});
}
</script>
Controller:
function MyCtrl ($scope, $http, $sce, Notification) {
$scope.username = context.targetUsername;
$scope.content_options = {
'yes' : 'Your value is True. To set it to False, <a onClick="callAngular(\'myToggleFunction\', \'false\')">click here</a>.',
'no' : 'You haven\'t done the thing, to do the thing, <a onClick="callAngular(\'myDifferentFunction\', \'someValue\')">click here</a>.'
}
$http.get(
'/api/v1/user/' + $scope.username + '/?fields=myBooleanField' // django rest api call
).then(function(response) {
$scope.user = response.data;
if ($scope.user.myBooleanField) {
$scope.html_content = $sce.trustAsHtml($scope.content_options['yes']);
} else {
$scope.html_content = $sce.trustAsHtml($scope.content_options['no']);
}
});
});
$scope.myToggleFunction = function(paramValue) {
// toggle value in the db
if (accepted === 'true') {
var success = "You turned on the thing";
var content = "yes";
} else {
var success = "You turned off the thing";
var content = "no";
}
$http({
method: 'GET',
url: '/api/v1/user/' + $scope.username + '/my_boolean_field/?value=' + paramValue, // django rest api call
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function(response) {
$scope.html_content = $sce.trustAsHtml($scope.content_options[content]);
Notification.success(success);
}, function(response) {
Notification.error("There was an error.");
});
};
$scope.myDifferentFunction = function(someOtherParamValue) {
// do some other stuff
};
}
MyCtrl.$inject = ['$scope', '$http', '$sce', 'Notification'];
You can use ngShow and ng-hide for show and hide HTML dynamic
<div ng-show="DBvalue">Your value is True. To set it to False, <a ng-click="myToggleFunction('paramValueFalse')">click here</a>.</div>
<div ng-hide="DBvalue">You haven't done the thing, to do the thing, <a ng-click="myDifferentFunction('someOtherParamValue')">click here</a>.</div>

How to refresh the table records automatically in angularjs after an event occurs

I am working on a Web App , using Laravel as backend API and AngularJS for frontend. I have successfully fetched the data from Laravel API and displayed it via AngularJS ng-repeat . Now i want a delete button for each record which is displayed in the table.When a user click that delete button it should delete the clicked record.I did the following try which is working perfectly.But the problem occurs when i click delete button it deletes record from database but it is not refreshing the records list , instead of refreshing it just shows the headers titles of table and nothing else. When i manually refresh it from browser then it displays back the records list.I want to load the list automatically after the record is deleted.
Summary: On delete i want to load / refresh the list automatically which is not happening right now.
Student Controller:
public function destroy($id)
{
$student = Student::find(Input::get('id'));
if($student){
$student->delete();
return Response::json(['destroy'=>true]);
}
}
app.js
var myApp = angular.module('myApp', ['mainCtrl', 'myAppService']);
myappservice.js
angular.module('myAppService', [])
.factory('Result', function($http) {
return {
get : function() {
return $http.get('api/result');
},
show : function(id) {
return $http.get('api/result/' + id);
},
save : function(resultData) {
return $http({
method: 'POST',
url: 'api/result',
headers: { 'Content-Type' : 'application/x-www-form-urlencoded' },
data: $.param(resultData)
});
},
destroy : function(id) {
return $http.delete('api/result/' + id,{params: {id}});
}
}
});
MainCtrl.js
angular.module('mainCtrl', [])
.controller('mainController', function($scope, $http, Result) {
// object to hold all the data for the new comment form
$scope.resultData = {};
// loading variable to show the spinning loading icon
$scope.loading = true;
// get all the comments first and bind it to the $scope.comments object
Result.get()
.success(function(data) {
$scope.students = data;
$scope.loading = false;
});
// function to handle submitting the form
$scope.submitResult = function() {
$scope.loading = true;
// save the comment. pass in comment data from the form
Result.save($scope.resultData)
.success(function(data) {
$scope.resultData = {};
// if successful, we'll need to refresh the comment list
Result.get()
.success(function(getData) {
$scope.students = getData;
$scope.loading = false;
});
})
.error(function(data) {
console.log(data);
});
};
// function to handle deleting a comment
$scope.deleteResult = function(id) {
$scope.loading = true;
Result.destroy(id)
.success(function(data) {
// if successful, we'll need to refresh the comment list
Result.get()
.success(function(getData) {
$scope.students = getData;
$scope.loading = false;
});
});
};
});
View
<table class="table table-striped">
<thead>
<tr>
<th>Roll No</th>
<th>Student Name</th>
<th>Father Name</th>
<th>Obtained Marks</th>
<th>Total Marks</th>
<th>Percentage</th>
<th>Delete</th>
</tr>
</thead>
<tbody ng-hide="loading" ng-repeat="student in students | filter:searchText">
<tr>
<td>#{{ student.rollno }}</td>
<td>#{{ student.name }}</td>
<td>#{{ student.fname }}</td>
<td>#{{ student.obtainedmarks }}</td>
<td>#{{ student.totalmarks }}</td>
<td>#{{ student.percentage }}</td>
<td>
Delete</p>
</td>
</tr>
</tbody>
</table>
Console Error: DELETE
http://localhost/ngresulty/public/api/result/30?id=30 500 (Internal
Server Error)
My guess would be that the Result.get() call is not returning the right data. You may want to check that endpoint.
Instead of making another call though, since you know that on deletion there was a success event, you could just change the scope then and not call your backend again, ie:
// function to handle deleting a comment
$scope.deleteResult = function(id) {
$scope.loading = true;
Result.destroy(id)
.success(function(data) {
// do something with data if you want to
$scope.students.splice(id, 1);
});
};
if you get 500 server error Result.destroy() calls error function. Its not success
$scope.loading = true;
Result.destroy(id)
.success(function(data) {
// if successful, we'll need to refresh the comment list
Result.get()
.success(function(getData) {
$scope.students = getData;
$scope.loading = false;
});
})
.error(function(data) {
// its not successful but i want call Result.get() anyway
Result.get()
.success(function(getData) {
$scope.students = getData;
$scope.loading = false;
});
}));
};
Try to put
$scope.$apply();
statement at the end of event

ng-repeat not reflecting when the source variable gets modified

I have the following angularJs code. When my source data changes, my ng-repeat does not update my view. I looked at other posts and added $scope.$apply(); , $scope.$digest(); at the end of my ajax success callback, but it did not help. The idea is that the page will have an empty table in the begining and after the ajax call onReady() it will populate the rows with data. Could someone point me at what I am missing here or a better way to achieve the same
JS:
(function() {
var app = angular.module("jmeter-module", []);
app.controller('JmeterTableController', ['$scope', function($scope){
$scope.data = [];
$(document).ready(function(){
var url = "jmeterTableData.html";
fetchTableData(url, 10, 25);
});
function fetchTableData(url, minAge, maxAge){
$.ajax({
type: "GET",
url: url,
data: { minAge : minAge,
maxAge : maxAge },
datatype: "json",
success: function(data){
/*If I try to print data here, I see the values rightly getting returned*/
$scope.data = data;
},
error: function(e){
console.log(e);
}
});
}
}]);
})();
JSP:
<div id="buildGroupsParentDivId" ng-controller="JmeterTableController as row">
.
.
.
<tbody id="jmeter-table-content" >
<tr ng-repeat="val in row.data">
<td><img title="History" src="/images/history.png" width="20" height="20"></td>
<td><input type="checkbox" value="save"></td>
<td>{{val.firstName}}</td>
<td>{{val.lastResult}}</td>
</tr>
</tbody>
the problem is with the execution of $.ajax outside of the scope of Angular's digest cycle. You could get this working with $scope.$apply, but since you're already using Angular it's better to use the AngularJS xhr helper methods:
https://docs.angularjs.org/api/ng/service/$http
// Simple GET request example :
$http.get('/someUrl').
then(function(response) {
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});

Searching a JSON file in AngularJS

So I have the following service/factory setup that should query a JSON file:
myappServices.factory('Result', ['$resource',
function($resource){
return $resource('search.json', {}, {
query: { method:'GET', params: {}, isArray:true }
});
}]);
And then in my SearchCtrl I have:
myappControllers.controller('SearchCtrl', function($rootScope, $scope, $state, Result, $location) {
$scope.query = ($state.includes('search') ? $location.search()['q'] : '');
$scope.filterQuery = ($state.includes('search') ? $location.search()['q'] : '');
$scope.queryChanged = function () {
if($scope.query){
$state.go('search', {'q': $scope.query} );
$scope.results = Result.query();
} else {
$location.search('q', null);
}
$scope.filterQuery = $scope.query;
}
if($scope.query){
$scope.results = Result.query();
} else {
$location.search('q', null);
}
});
The search results page looks like:
<ul class="ng-hide" ng-show="results.length > 0">
<li ng-repeat="result in results">
{{result.name}}
<p>{{result.snippet}}</p>
</li>
</ul>
<p class="ng-hide" ng-show="results.length == 0">Nothing here!</p>
If I do a query like:
/search?=hello
Then ALL the results in the JSON file are returned REGARDLESS of what I have searched for (should only be showing results that match).
However if I don't do a query then no results are shown (correct behaviour).
Why is the query not being used against the JSON file to filter the results?
I did originally have the repeater as:
<li ng-repeat="result in results = ( results | filter:filterQuery )">
But that would be incorrect in this case, as it's would be saying show me the results but filter them using the filterQuery. But in reality there would never be any results if no query, hence removing this part of the repeater. As I don't want to filter a list but return a list filtered.
So it would seem I am missing something in my service to handle this.
Edited: Simplify your resource so it doesn't define query:
myappServices.factory('Result', ['$resource',
function($resource){
return $resource('search.json');
}]);
Try changing your controller to clear the results and only set them when a query has been made:
myappControllers.controller('SearchCtrl', function($rootScope, $scope, $state, Result, $location) {
$scope.query = ($state.includes('search') ? $location.search()['q'] : '');
$scope.filterQuery = ($state.includes('search') ? $location.search()['q'] : '');
$scope.results = [];
$scope.queryChanged = function () {
if($scope.query){
$state.go('search', {'q': $scope.query} );
} else {
$scope.results = Result.query({q:$scope.query});
}
$scope.filterQuery = $scope.query;
}
});
app.factory('querySvc', ['$q', '$resource', querySvc]);
function querySvc($q, $resource) {
var service = {
getSearch: getSearch
}
return service;
function getSearch(){
var deferred = $q.defer();
$resource('search.json').query( function(data){
deferred.resolve(data);
}, function(error){
// handle error
});
return deferred.promise;
}
Then you can call it that way:
querySvc.getSearch().then(function (data){
$scope.results = data;
}, function(error){
//handle error here
});
About your first note :
When you instantiate your controller, it executes everything inside it.
So, if you want to call this function when a user clicks somewhere, you can register a function into the scope :
$scope.querySearch = function(){
querySvc.getSearch().then(function (data){
$scope.results = data;
}, function(error){
//handle error here
});
}
And then, into your view, you will have to call it when an event is fired.
I don't remember the event you need, but I'll show you a way of doing it :
<span ng-click="querySearch()" />
This will trigger the function when you click on the span.
In the console, look in the Network tab to see if the request has an error and/or to see the contents of the response. If the response has a status code of 200 and the contents are correct, then check the response to see if the data is structured correctly for results.length (that it returns a list).

AngularJS: display a select box from JSON data retrieved by http GET (from REST service)

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);
});
}
});

Categories