I am trying to implement ngTable to display json from rest call. In my factory js, I defined the method for the http get request to obtain all records in this case.
ristoreApp.factory("fmFactory", ['$http', '$window',
function ($http, $window) {
var service = {};
service.getAll = function () {
var url = SERVER + "/ristore/foundation/";
return $http({
headers: {'Authorization': 'Bearer ' + $window.localStorage.getItem("access_token")},
url: url,
method: 'GET',
crossOrigin: true
})
}
return service;
}]);
ngTable is set up in my controller js
ristoreApp.controller("fmCtrl",
['$scope', 'fmFactory', 'NgTableParams', function($scope, fmFactory, NgTableParams) {
$scope.selection = '0';
$scope.reports = [];
$scope.fmSearch = function () {
if ($scope.selection == '0') {
fmFactory.getAll().success(function (data) {
$scope.reports = data;
$scope.tableParams = new NgTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
total: $scope.reports.length, // length of data
getData: function ($defer, params) {
$defer.resolve($scope.reports.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
})
}
}
}]
)
Nothing fancy, just simple pagination with 10 records per page. However, I got error TypeError: Cannot read property 'page' of undefined for the method params.page() in $defer.resolve(). This is very strange. Apparently 'page' is defined in the parameter section of NgTableParams. Why does it complain it is not defined?
EDIT:
Based on the link from Sergii's answer I removed the $defer and changed my controller js to the following:
ristoreApp.controller("fmCtrl",
['$scope', 'fmFactory', 'NgTableParams', function($scope, fmFactory, NgTableParams) {
$scope.selection = '0';
$scope.reports = [];
$scope.fmSearch = function () {
if ($scope.selection == '0') {
$scope.tableParams = new NgTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
getData: function (params) {
return fmFactory.getAll().then(function(data) {
params.total(data.inlineCount);
return data;
});
}
});
}
}
}]
)
And yet nothing displayed but a bunch of lines. The http call url has been tested and returns correct promise using rest api tester.
As I wrote in comment params is undefined, but exception was wrapped\processed by angular that is reason of partly correct exception information.
I believe this problem appears because of you are using newest ng-table-1.0.0 library for now. If you navigate to Angular ngTableDynamic example:server-side list or Angular ngTable example:server-side list please pay attantion that API to load data was changed.
getData: function(params) {
// ajax request to api
return yourFactory.get(params.url()).$promise.then(function(data) {
params.total(data.inlineCount);
return data.results;
});
}
In your parameter $defer also different object (object is params). If you'll try apply provided solution, please make sure that you changed correctly parameters:
params.url() - should be pagination filter like {page: 0, size: 10}
data.inlineCount - total elements on server side
data.results - data list from server side
I hope my investigation helped not only me to fix this problem.
Related
I am trying to implement server side pagination in Angular JS .I have created my server response consisting of results and a variable called isMoreResults . When isMoreResults is true I can safely assume that it has more results to be displayed . Now how can I go about creating a pagination on angular Js side ,such that I can display PREVIOUS and NEXT . when user clicks on next I can call the server to fetch next response .Any leads on how can I implement and what is the correct format to implement pagination ? I have went through many sites and majoritily I could see client side validation .should I also switch to client side validation ?
You can do something like this.
angular.module('app', ['ui.bootstrap']);
angular.module('app').controller('PaginationDemoCtrl', function($scope, $http) {
$scope.currentPage = 1;
$scope.limit= 10;
$scope.tracks = [];
getData();
function getData() {
$http.get("https://api.spotify.com/v1/search?query=iron+&offset="+($scope.currentPage-1)*$scope.limit+"&limit=20&type=artist")
.then(function(response) {
$scope.totalItems = response.data.artists.total
angular.copy(response.data.artists.items, $scope.tracks)
});
}
//get another portions of data on page changed
$scope.pageChanged = function() {
getData();
};
});
in html
<div ng-controller="PaginationDemoCtrl">
<h4>Sample Server Pagination</h4>
<uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()" items-per-page="100"></uib-pagination>
</div>
Usually do this for pagination:
1 - Create an API that takes how many SKIP and how many TAKE .. somehting like:
http://www.test.com/api/Users?skip=0&take=10
Then:
2 - in Angular install this plugin: (maybe with bower or npm)
https://github.com/brantwills/Angular-Paging
3 - in your HTML something like:
<paging page="currentPage"
page-size="pageSize"
total="total"
paging-action="DoPaging(page, pageSize, total)">
</paging>
4 - In your Controller:
/**
* MainCtrl - controller
*/
"use strict";
angular
.module('demo')
.controller('UsersCtrl', [
"$scope",
"User",
"$state",
"$timeout",
function (
$scope,
User,
$state,
$timeout) {
var vm = this;
// Binded Functions
$scope.currentPage = 1;
$scope.pageSize = 10;
$scope.DoPaging = _doPaging;
// Page Load
init();
/////////////////////////////////
// PRIVATE FUNCTION
////////////////////////////////
//Page Load
function init() {
$scope.promUsers = User.GetUsers(0, 10).$promise.then(function (resp) {
vm.users = resp;
$scope.total = resp[0].total //<-- put in your Back End Entities a field where you store how many record you have on your DB (all)
}).catch(function (err) {
console.log(err);
});
}
function _doPaging(text, page, pageSize, total) {
var skip = pageSize * (page -1);
User.GetUsers(skip, pageSize).$promise.then(function(resp){
vm.users = resp;
$scope.total = resp[0].total
}).catch(function (err) {
console.log(err);
});
}
////////////////////////////////
}]);
5 - In your service:
"use strict";
angular
.module("demo")
.factory("User", [
"$resource",
"$q",
"enviroment",
"$timeout",
function (
$resource,
$q,
enviroment,
$timeout) {
// Private Filed
var _serviceBase = "api/User/";
// Private Method
//skip and take in query string
var resource = $resource(enviroment.apiUrl + _serviceBase, { skip: '#skip', take: '#take' }, {
query: { method: "GET", isArray: true },
create: { method: "POST", isArray: false },
update: { method: "PUT", isArray: false }
});
// Public Method
return {
GetUsers: function (skip, take) {
return resource.query({ skip: skip, take: take });
}
};
}]);
First i want to say that i'm fairly new to AngularJS, so it might be that i'm asking my question with some oddnes.
I'm trying to pass a string to a factory which in return gives me a result from my database. When i hardcode the values, everything works. But when i try to pass inn values from my view/controller things stop working.
Here is my factory:
healthServices.factory('Entry',
function ($resource) {
return $resource('http://localhost:60673/api/hierarchy/:id', { id: '#id'}, {
query: { method: 'GET', isArray: true }
});
});
Here is the controller im using:
$scope.changeData = function (type) {
$scope.entry = new Entry();
$scope.id = type;
$scope.healthData = $scope.entry.$query({ id: $scope.id });
}
And this is how it looks in my html:
<button ng-click="changeData('someValue')">
At the moment i keep getting
TypeError: value.push is not a function
As i mentioned im quite new to this, so I might be far off. Any help would be very much appreciated.
What is intended by this line of code?
$scope.entry = new Entry();
Entry is your service you want to call.
You should pass this into your controller via dependency injection.
Angular does the 'new' for you.
myApp.controller('myCntrl', HomeCtrl);
HomeCtrl.$inject = ['$scope', 'Entry'];
function HomeCtrl($scope, Entry) {
...
}
I am not seeing any wrong with your $resource configuration.
var myApp = angular.module('myApp',['ngResource']);
myApp.factory('Entry', function ($resource) {
return $resource('http://localhost:60673/api/hierarchy/:id', { id: '#id'}, {
query: { method: 'GET', isArray: true }
});
});
myApp.controller('myCtrl', ['$scope', 'Entry', function($scope, Entry){
$scope.changeData = function (type) {
$scope.entry = new Entry();
$scope.id = type;
$scope.healthData = $scope.entry.$query({ id: $scope.id });
}
}]);
i am getting below in console
GET http://localhost:60673/api/hierarchy/someValue
error lies on other part of the code, please post your controller completely.
This seems so simple yet I have been banging my head against it for hours now...
I have an angular 1.2.29 module that uses ui-router to resolve a data dependency from a service and inject it into a named nested view controller. The service returns my data object exactly as desired and I can inject that returned data into my view controller and log it again with no issue.
What I am baffled by is that if I inspect the object in the console, I can see the array I am trying to access on one of the nested properties of the object I injected from the service (this is from within the context of the controller I am trying to use it in).
However, if I try and pass that array of data into another method in the same controller (in this case a d3 method to draw a graph) - I get nothing but a empty string where the array is.
I can access every other property on the object I passed into the method just fine, and so long as I don't try and access the array directly it is 'visible' to me in my logging, but when I try and pass the array directly in I get an empty string.
I have tried everything I can from making a deep copy or pushing the array into a new object and accessing it that way, trying to cast that property as an array (again into a new object), and a whole host of other random hacks I have run across all to no avail.
I feel like I am missing something incredibly trivial here...
These three logs are called on the same object from within the same method one after the other and you can see the issue.
I am trying to pass the data.datasources.tabledata.datum array to another method that expects an array, but all I can get is the empty string.
Note: 'data' is the object returned from the service resolve at the state transition and injected into the view controller.
> console.log(data);
// Expected object.
> Object {datasources: Object}
datasources: Object
tabledata: Object
datum: Array[9383]
source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv"
__proto__: Object
__proto__: Object
__proto__: Object
> console.log(data.datasources);
// Expected object.
> Object {tabledata: Object}
tabledata: Object
datum: Array[9383]
source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv"
__proto__: Object
__proto__: Object
> console.log(data.datasources.tabledata);
// String instead of array... WTH???
> Object {source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv", datum: ""}
> console.log(data.datasources.tabledata.datum);
// Returns NOTHING - not even undefined... just an empty log line #_#
>
UPDATE: Here is the service loading the data (it is my own)
(function() {
'use strict';
angular
.module('mcsdss.providers')
.factory('FormulationRetrieval', FormulationRetrieval);
FormulationRetrieval.$inject = ['$http'];
function FormulationRetrieval($http) {
FormulationRetrieval.getFormulation = function (target) {
var promise = $http
.get(target)
.then(function (response) {
return FormulationRetrieval.configureFormulation(response.data);
});
return promise;
};
FormulationRetrieval.configureFormulation = function (f) {
FormulationRetrieval.formulationContainer = f;
FormulationRetrieval.loadFormulationSourceData(FormulationRetrieval.formulationContainer);
FormulationRetrieval.loadFormulationGisData(FormulationRetrieval.formulationContainer);
return FormulationRetrieval.formulationContainer;
};
FormulationRetrieval.loadFormulationSourceData = function (fc) {
function parseFormulationDatasource(fd, destination) {
Papa.parse(fd, {
complete: function(results) {
destination.datum = results.data;
}
});
}
function loadData(target) {
var promise = $http
.get(target.source)
.then(function (response) {
parseFormulationDatasource(response.data, target);
});
return promise;
}
var datasources = [fc.datagridConfig.datasources.tabledata, fc.graphConfig.datasources.graphContextData];
angular.forEach(datasources, loadData);
};
FormulationRetrieval.loadFormulationGisData = function (fc) {
function loadGeodata(target) {
angular.forEach(target, function(value, key) {
var promise = $http
.get(value.source)
.then(function (response) {
value.datum = response.data;
});
return promise;
});
}
var datasources = [fc.mapConfig.datasources.geojson];
angular.forEach(datasources, loadGeodata);
};
FormulationRetrieval.getAnalysisConfig = function (fc) {
var analysisConfig = fc.analysisConfig;
return analysisConfig;
};
FormulationRetrieval.getMaufConfig = function (fc) {
var maufConfig = fc.maufConfig;
return maufConfig;
};
FormulationRetrieval.getGraphConfig = function (fc) {
var graphConfig = fc.graphConfig;
return graphConfig;
};
FormulationRetrieval.getTableConfig = function (fc) {
var tableConfig = fc.datagridConfig;
return tableConfig;
};
FormulationRetrieval.getMapConfig = function (fc) {
var mapConfig = fc.mapConfig;
return mapConfig;
};
return FormulationRetrieval;
}
})();
Here is the ui-router resolve on the state:
(function() {
'use strict';
angular
.module('analyze')
.config(DashboardRoutes);
DashboardRoutes.$inject = ['$stateProvider'];
function DashboardRoutes($stateProvider) {
// Define states.
var analyze_state = {
abstract: true,
url: '/analyze',
templateUrl: 'modules/analyze/views/analyze.client.view.html',
controller: 'AnalyzeViewController',
controllerAs: 'analyze',
data: {
title: 'Analyze'
},
resolve: {
analysisData: function(FormulationRetrieval) {
return FormulationRetrieval.getFormulation('./data/formulations/bs.formulation.json');
},
analysisConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getAnalysisConfig(analysisData);
},
maufConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getMaufConfig(analysisData);
},
tableConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getTableConfig(analysisData);
},
graphConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getGraphConfig(analysisData);
},
mapConfig: function(FormulationRetrieval, analysisData) {
return FormulationRetrieval.getMapConfig(analysisData);
}
}
};
var analyze_layout_state = {
abstract: false,
url: '',
views: {
'graph': {
templateUrl: 'modules/analyze/views/analyze.graph.client.view.html',
controller: 'GraphViewController'
},
'map': {
templateUrl: 'modules/analyze/views/analyze.map.client.view.html',
controller: 'MapViewController'
},
'filters': {
templateUrl: 'modules/analyze/views/analyze.filters.client.view.html',
controller: 'FiltersViewController',
controllerAs: 'filters'
},
'datatable': {
templateUrl: 'modules/analyze/views/analyze.datatable.client.view.html',
controller: 'DatatableViewController',
controllerAs: 'datatable'
}
}
};
// Populate provider.
$stateProvider
.state('analyze', analyze_state)
.state('analyze.layout', analyze_layout_state);
}
})();
And here is the controller receiving the injected data:
(function() {
'use strict';
angular
.module('analyze')
.controller('AnalyzeViewController', AnalyzeViewController);
AnalyzeViewController.$inject = ['$rootScope', '$scope', '$state', '$location', 'Authentication', 'httpq', 'analysisData', 'maufConfig', 'tableConfig', 'graphConfig', 'mapConfig'];
function AnalyzeViewController($rootScope, $scope, $state, $location, Authentication, $httpq, analysisData, analysisConfig, maufConfig, tableConfig, graphConfig, mapConfig) {
// This provides Authentication context.
$scope.authentication = Authentication;
$scope.currentRoute = 'Analyze';
// console.log($scope.currentRoute);
// ALL OF THESE ARE INJECTED AND APPEAR TO BE CORRECT IN CONSOLE.
// console.log(analysisData);
// console.log(analysisConfig);
// console.log(maufConfig);
// console.log(tableConfig);
// console.log(graphConfig);
// console.log(mapConfig);
// NOTE: At one point I was loading the data through promises inside the
//controller, but moved it into the state resolve for better SOC.
//Strangely the $broadcast of the exact same value done here in the finally()
//block of the $httpq method works - using the new injected data object!
//And yet, the same $broadcast on $stateChangeSuccess (which DOES send the correct
//data into the listening subscribers if I only send the entire object) sends only
//empty string if I specify the array.
// Manual data loading.
$scope.sourceFile_A = './data/BSGAM_Heads_Wells_Drains_Zones_Master.csv';
$httpq.get($scope.sourceFile_A)
.then(function(data) {
// ...removed because not used.
})
.catch(function(data, status) {
console.error('Load error', response.status, response.data);
})
.finally(function() {
// Works here using the injected resolved promise, does not work in stateChangeSuccess... WTH??
$scope.$broadcast('analysisDataLoaded', analysisData.datagridConfig.datasources.tabledata.datum);
});
$scope.$on('$stateChangeSuccess', function() {
// EXACT SAME BROADCAST AS ABOVE FAILS HERE - EMPTY STRING.
// $scope.$broadcast('analysisDataLoaded', analysisData.datagridConfig.datasources.tabledata.datum);
});
// extra code removed...
})();
Lastly the Nested View Controller (one of 4) that I am trying to get to use the data via the $broadcast from the abstract parent. I can also access all the injected objects here directly without the broadcast but I am trying to decouple as much as possible as several views need to update in sync from the broadcast.
(function() {
'use strict';
angular
.module('analyze')
.controller('DatatableViewController', DatatableViewController);
DatatableViewController.$inject = ['$scope', 'Authentication', '$filter', 'ngTableParams', 'AnalysisDataFactory', 'tableConfig'];
function DatatableViewController($scope, Authentication, $filter, ngTableParams, AnalysisDataFactory, tableConfig) {
// This provides Authentication context.
$scope.authentication = Authentication;
// Expose public API.
$scope.headerFilter = headerFilter;
$scope.datasetOrder = datasetOrder;
$scope.rowClicked = rowClicked;
$scope.decorateSiblings = decorateSiblings;
$scope.clearSiblings = clearSiblings;
$scope.updateView = updateView;
// Private members.
$scope.headerdata = [];
$scope.tabledata = [];
$scope.suf01 = 0;
$scope.suf02 = 0;
$scope.suf03 = 0;
$scope.muf = 0;
$scope.$on('analysisDataLoaded', function(event, args) {
console.log('analysisDataLoaded...', event, args);
$scope.updateView(args);
// THIS IS WHAT I WANT TO DO INSTEAD:
// $scope.updateView(tableConfig); // Not working yet.
});
function headerFilter(target) {
return target.visible;
}
function datasetOrder(key) {
angular.forEach($scope.headers, function(target) {
// console.log('key='+key);
if (target.data == key) {
if (target.visible) {
return target.order;
}
}
});
return -1;
}
function rowClicked(target) {
for (var key in target) {
if (target.hasOwnProperty(key)) {
console.log(key + ' -> ' + target[key]);
}
}
}
function decorateSiblings(target) {
// console.log('data row touched, sending emission.');
$scope.$emit('currentDatatableTarget', target);
}
function clearSiblings(target) {
// console.log('datarow cleared, sending all clear.');
$scope.$emit('clearDatatableTarget', target);
}
function updateView(data) {
// ngTable
$scope.dataTable = new ngTableParams({
page: 1,
count: 10
}, {
total: $scope.tabledata.length,
counts: [10, 25, 50, 100, 250],
defaultSort: 'asc',
getData: function($defer, params) {
$scope.data = params.sorting() ? $filter('orderBy')($scope.tabledata, params.orderBy()) : $scope.tabledata;
$scope.data = params.filter() ? $filter('filter')(data, params.filter()) : data;
$scope.data = data.slice((params.page() - 1) * params.count(), params.page() * params.count());
$defer.resolve($scope.data);
}
});
}
}
})();
Something simple in here - probably a JS gotcha that I am overlooking.... Thanks in advance for any input!!
Turns out it was from not properly deferring and resolving the promises before trying to bind to them. I still have not identified exactly where that timing is going wrong in my code, but I can now intermittently load the data as I intended and can see that it is clearly a function of async inconsistencies. The $broadcast event is working just fine (when the async data loads properly).
I am a novice with Angular. I have a project that a friend and I were working on that acts as a "Reef Tank Controller". It is an arduino/mysql database/angularJS page. My buddy was working on the front end but had to drop out due to work and now I have a semi completed website. From the perspective of the back-end it all works. On the front end I wanted to add a section to control the lighting. I wanted to start out by simply displaying the values of each LED color that is stored in the database. I created a new controller for each LED string I want to display the value of:
'use strict';
/* Controllers */
angular.module('reefController.controllers', ['uiSlider'])
// Main Dashboard Controller
.controller('dashboardController', ['$scope', function($scope) {
$.ajax({
url: 'php/reef_controller_selectVals.php',
type: 'json',
success: function(data) {
reefController.config.data = data;
// Draw Charts
$.each(data.charts, function(name, chartData) {
$(chartData.series).each(function(idx, val){
// Reformat sensor names to their Human readable versions
this.name = reefController.labels[name].sensors[idx];
});
var options = $.extend(reefController.config.chartOptions, {
chart: {
events: {
load: function() {
var chart = this;
setInterval(function() {
$.ajax({
url: 'php/reef_controller_selectVals.php?incremental=' + chart.options.name,
success: function(data) {
// Only add data points if the latest date in the DB is different
if (!(chart.xAxis[0].dataMax == data.timestamp)) {
$(chart.series).each(function(i, series) {
series.addPoint([data.timestamp, data[series.options.id]], true, true);
});
}
}
});
}, reefController.config.timers.chartUpdateInterval);
}
},
renderTo: name
},
name: name,
series: chartData.series,
title: { text: reefController.labels[name].chart.title }
});
var chart = Highcharts.StockChart(options);
reefController.config.charts[name] = chart;
});
//Set LED Value Labels
// Set outlets
var i = 0;
$('.outlet').each(function(){
if (!$(this).hasClass('constant')) {
$(this).attr('data-outlet-id', data.outlets[i].id)
.attr('data-reveal-id', 'date-time-modal')
.attr('data-on-time', data.outlets[i].on_time)
.attr('data-off-time', data.outlets[i].off_time);
if (data.outlets[i].active) {
$(this).addClass('active');
} else {
$(this).removeClass('active');
}
i++;
// Bind click event to outlets
$(this).click(function(){
var outlet = $(this);
var id = outlet.attr('data-outlet-id');
var newOutletState = (outlet.hasClass('active')) ? 0 : 1;
// Set datepickers to currently defined DB times
$('#on_time').val(outlet.attr('data-on-time'));
$('#off_time').val(outlet.attr('data-off-time'));
// Formatter function for date time pickers
var dateFormatter = function(elem, current_time) {
elem.html(moment(current_time).format('ddd M/D/YY HH:mm'));
};
$('#on_time').datetimepicker({
format: 'Y-d-m H:i:s',
inline: true,
defaultDate: outlet.attr('data-on-time'),
onChangeDateTime: function(current_time) { dateFormatter($('#on_time_display'), current_time) },
onGenerate: function(current_time) { dateFormatter($('#on_time_display'), current_time) }
});
$('#off_time').datetimepicker({
format: 'Y-d-m H:i:s',
inline: true,
defaultDate: outlet.attr('data-off-time'),
onChangeDateTime: function(current_time) { dateFormatter($('#off_time_display'), current_time) },
onGenerate: function(current_time) { dateFormatter($('#off_time_display'), current_time) }
});
// Submit Button
$('#submit-btn').click(function() {
data = {
'id': id,
'on_time': $('#on_time').val(),
'off_time': $('#off_time').val(),
'active': newOutletState
};
$.ajax("php/reef_controller_loadOutlet.php", {
type: 'POST',
data: data,
dataType: 'json',
success: function(data) {
outlet.toggleClass('active');
}
});
$('#date-time-modal').foundation('reveal', 'close');
});
// Cancel Button
$('#cancel-btn').click(function(){
$('#date-time-modal').foundation('reveal', 'close');
});
});
}
});
}
});
}])
.controller('outletController', ['$scope', function($scope) {
$.ajax({
url: 'img/outlet.svg',
success: function(svg) {
var svgDoc = document.importNode(svg.documentElement, true);
$('.outlet').append(svgDoc);
}
});
}])
.controller('temperatureController', ['$scope', function($scope) { }])
.controller('phController', ['$scope', function($scope) { }])
.controller('whiteLedCtrl', ['$scope', function($scope) {}])
.controller('blueLedCtrl', ['$scope', function($scope) {}])
.controller('variousColorLedCtrl', ['$scope', function($scope) {}]);
In my dashboard.html file I have:
<table style="width: 100%;">
<tr>
<td>
{{ ledValues }}
</td>
</tr>
<tr>
<td colspan="3" style="background: #eff4f6;"><input type="checkbox" name="overrideLightingSchema" value="overRide">
Override Current Lighting Pattern
</td>
</tr>
<tr>
<td colspan="3">
<select name="lightingSchemas">
<option value="feedingLightingPattern">Feeding Schema</option>
<option value="morningLightingPattern">Morning Schema</option>
<option value="standardLightingPattern">Standard Schema</option>
<option value="twilightLightingPattern">Dusk Schema</option>
<option value="nightTimeLightingPattern">Night Schema</option>
<option value="deepNightLightingPattern">Late Night Schema</option>
</select>
</td>
</tr>
</table>
Sometimes this displays the values from the database other times it just says:
{{ ledValues }}
It may be an async issue of some sort but my angular, well JS for that matter, is weak. Any help would be great.
The main issue I can see here is that you are using $ajax to make requests to the server.
You use the response from the server to set your variable...
reefController.config.data = data;
However since $ajax is not part of Angular this update occurs outside of the scope digest. Therefore Angular does not know to update the binding. You could try wrapping you assignment in $apply.
$scope.$apply(function(){
reefController.config.data = data;
});
That said, I cannot see where reefController is defined. You probably want to be assigning it to the scope:
$scope.$apply(function(){
$scope.MyData = data;
});
However, I would actually recommend you replace the $ajax calls with the Angular $http service.
//inject $http
.controller('dashboardController', ['$scope', '$http', function($scope, $http) {
//then use it later on
$http({
method: 'GET',
url: 'php/reef_controller_selectVals.php'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
$scope.MyData = data;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Example
Below Is a very quick example of how to use $http to get the data from the server.
The full example, including a fake service (that does not require a server response) can be found here: http://codepen.io/anon/pen/GogWMV
'use strict';
angular.module('reefApp', [ 'uiSlider']);
/* CONTROLLER */
angular.module('reefApp')
.controller('dashboardController', dashboardController);
/* Define controller dependencies.
Note how we do not use $scope as we are using controller as vm syntax
And we assign our scope variables to 'ctrl' rather than scope directly.
see john papa styleguide (https://github.com/johnpapa/angular-styleguide#style-y032)
*/
dashboardController.$inject = ['ledService'];
function dashboardController(ledService)
{
var ctrl = this;
//define an array to hold our led data
ctrl.ledData = [];
//call method to get the data
getLedData();
//this method uses our service to get data from the server
//when the response is received it assigns it to our variable
//this will in turn update the data on screen
function getLedData()
{
ledService.getLedData()
.then(function(response){
ctrl.ledData = response.data;
});
}
}
/* LED SERVICE */
/* the service is responsible for calling the server to get the data.
*/
angular.module('reefApp')
.factory('ledService', ledService);
ledService.$inject = ['$http'];
function ledService($http)
{
var endpointUrl = "http://addressOfYourEndpoint";
/* expose our API */
var service = {
getLedData: getLedData,
}
return service;
function getLedData()
{
/* this is how you would call the server to get your data using $http */
/* this will return a promise to the calling method (in the controller)
when the server returns data this will 'resolve' and you will have access to the data
in the controller:
Promises: http://andyshora.com/promises-angularjs-explained-as-cartoon.html*/
return $http.get(endpointUrl);
}
}
Taking this further, best practice would be to hold a reference to the data returned from the server inside the service; one reason is the service is a singleton - so this data service and it's data can be shared across controllers.
function ledService($http)
{
var endpointUrl = "http://addressOfYourEndpoint";
/* expose our API */
var service = {
ledData: [],
getLedData: getLedData,
}
return service;
function getLedData()
{
return $http.get(endpointUrl)
.then(function(response)
{
/* assign response to service variable, before promise is returned to caller */
service.ledData = response.data;
});
}
}
Then in our controller...
function getLedData()
{
ledService.getLedData()
.then(function(response){
ctrl.ledData = ledService.ledData;
});
}
Update
To collect more data, you could add a service for each piece of data you want to collect - or add more methods to existing service. Let's assume you add a phService.
You then inject this into your controller. And add call a new method to use the service to the data and assign to the model. It can then be shown in the view.
dashboardController.$inject = ['ledService', 'phService'];
function dashboardController(ledService, phService)
{
var ctrl = this;
//our data will be stored here
ctrl.ledData = [];
ctrl.phData = [];
//call methods to get the data
getLedData();
getPhData();
function getLedData()
{
ledService.getLedData()
.then(function(response){
ctrl.ledData = response.data;
});
}
function getPhData()
{
phService.getPhData()
.then(function(response){
ctrl.phData = response.data;
});
}
}
Then in the view (HTML):
<tr ng-repeat="ph in ctrl.phData">
<td> PHValue</td>
<td >
{{ ph }}
</td>
</tr>
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I got this angular factory:
var productApp = angular.module('productApp', ['ngRoute', 'LocalStorageModule', 'angularSlideables', 'ui.bootstrap']);
productApp.factory('productFactory', function($http, localStorageService, $q) {
var factory = {};
factory.getProductById = function(prod_id) {
if(prod_id !== '') {
$http({
url: 'rest/message/getProductById/' + prod_id,
method: 'GET'
}).success(function(data, status) {
return data;
}).error(function(data, status){
// do nothing
});
}else {
alert("There was an error while passing the ID. Please refresh the page and try again");
}
}
return factory;
});
Injecting the factory in a controller and calling to the "getProductById" function:
productApp.controller('ModalInstanceCtrl', function ($scope, $modalInstance, productFactory, prodId) {
console.log("this is the prod id " + prodId);
// search product in the database
$scope.prod = productFactory.getProductById(prodId);
console.log($scope.prod);
$scope.ok = function () {
console.log($scope.prodData);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
Now, don't know what's wrong with it... the function RETURNS the data because i did a console.log(data) and saw all the response, but in the controller if i inspect the $scope.prod, it's undefined. It's not returning the data back from the function.
(Just in case you guys ask, the "prodId" in the controller parameter is fine, and retrieving that from another controller)
How can i solve this? :(
Thanks in advance.
The pattern I have used to solve this problem is to pass in the success & error callback functions to the factory... like this:
var productApp = angular.module('productApp', ['ngRoute', 'LocalStorageModule', 'angularSlideables', 'ui.bootstrap']);
productApp.factory('productFactory', function($http, localStorageService, $q) {
var factory = {};
factory.getProductById = function(prod_id, successCallback, errorCallback) {
if(prod_id !== '') {
$http({
url: 'rest/message/getProductById/' + prod_id,
method: 'GET'
})
.success(successCallback)
.error(errroCallback);
} else {
alert("There was an error while passing the ID. Please refresh the page and try again");
}
}
return factory;
});
and then:
productApp.controller('ModalInstanceCtrl', function ($scope, $modalInstance, productFactory, prodId) {
console.log("this is the prod id " + prodId);
// search product in the database
productFactory.getProductById(prodId, function successCallback(data) {
$scope.prod = data;
}, function errorCallback(data, status) {
alert("An error occurred retrieving product. Please refresh the page & try again.");
});
console.log($scope.prod);
$scope.ok = function () {
console.log($scope.prodData);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
By doing it this way instead, you have access to the scope in the controller & can do whatever you need to with the returned data.
Here's what I do. I'm using $resournce instead of $http, but it should be enough to get you going. You may even want to use the $resource since it has the built in fns.
My factory:
.factory('WorkOrder', function($resource){
// $resource Usage: $resource(url[, paramDefaults][, actions]);
return $resource('/controller/get/:id.json', {}, {
/*
* By default, the following actions are returned; modify or add as needed
* { 'get': {method:'GET'},
* 'save': {method:'POST'},
* 'query': {method:'GET', isArray:true},
* 'delete': {method:'DELETE'} };
*/
});
})
My controller:
// get the work order data using the work order id from the tag attribute
var getWO = function() {
WorkOrder.get({woId:$attrs.id},
// success callback
function(response) {
// Assign the work order data to the scope
$scope.WorkOrder = response.WorkOrder;
},
// fail callback
function(response) {
// whatever...
}
);
};
getWO();
I put my success and fail fns in my controller because that's where I most likely know how I want to respond to success or failed calls. I also assign the function to a variable and then call it right after in case I want to include the fn call inside a $timeout or call it elsewhere.
Hope this answers your question.