ASP.NET MVC 6 Angular JS Table Update - javascript

I'm currently learning new MVC 6 and stucked completely with simple action - table data update on item selection change.The desired behaviour is to load questions that belong selected question block
I have angularJS factory:
(function () {
'use strict';
angular
.module('questionBlockApp')
.factory('questionBlockService', questionBlockService);
var questionBlockService = angular.module('questionBlockService', ['ngResource']);
questionBlockService.factory('Blocks', ['$resource',
function ($resource) {
return $resource('/api/blocks/', {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}]);
questionBlockService.factory('Questions', ['$resource',
function ($resource) {
return $resource('/api/blocks/:blockId', {blockId : '#blockId'}, {
query: { method: 'GET', params: {}, isArray: true }
});
}]);
})();
Controller, which has refresh func (loadQuestions) built inside selection change function:
(function () {
'use strict';
angular
.module('questionBlockApp')
.controller('questionBlockController', questionBlockController);
//.controller('questionController', questionController);
questionBlockController.$inject = ['$scope', 'Blocks', 'Questions'];
//questionController.$inject = ['$scope', 'Questions'];
function questionBlockController($scope, Blocks, Questions) {
$scope.selectedBlock = 2;
if ($scope.Blocks == undefined | $scope.Blocks == null) {
$scope.Blocks = Blocks.query();
}
$scope.setSelected = function (blockId) {
$scope.selectedBlock = blockId;
$scope.loadQuestions();
}
$scope.loadQuestions = function () {
$scope.data = Questions.query({ blockId: $scope.selectedBlock });
$scope.data.$promise.then(function (data) {
$scope.Questions = data;
});
};
$scope.loadQuestions();
}
})();
And views:
View from which setSelected is called:
<table class="table table-striped table-condensed" ng-cloak ng-controller="questionBlockController">
<thead>
...
</thead>
<tbody>
<tr ng-repeat="block in Blocks" ng-click="setSelected(block.Id)" ng-class="{'selected': block.Id == selectedBlock}">
<td>{{block.Id}}</td>
<td>{{block.Name}}</td>
<td>{{block.Created}}</td>
</tr>
</tbody>
</table>
<table id="test" ng-controller="questionBlockController">
<thead>
<tr>
...
</tr>
</thead>
<tbody>
<tr ng-repeat="question in Questions">
<td>{{question.Id}}</td>
<td>{{question.Text}}</td>
<td>{{question.TimeLimit}}</td>
<td>{{question.Updated}}</td>
</tr>
</tbody>
</table>
When I click on different items in QuestionBlock table, $scope.Questions is updated properly, but the table does not reflect changes, as if no binding exists.

Okay, I am just a bit damaged.
There are two questionBlockController controllers defined, leading to intialization of different scopes => two $scope.Questions objects existence => refresh occured in Blocks scope, which was undesired behaviour.

Related

Confusion on passing variables between controller and service in AngularJS

I just started learning AngularJS. I know this has been asked multiple times, but I am just not getting it. I've been at this for hours now, between reading example code here and fiddling about in my IDE.
I have the following code which is supposed to retrieve a section key for an item and then pass that key to a service which consumes an API to provide a response which populates the categories based on that key. This happens when a section table row is clicked.
The HTML
<div ng-controller="menuController">
<h1>AngularTest</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<tbody>
<tr ng-click="getSectionID($event)" ng-repeat-start="section in navSectionList" data-id="{{section.id}}">
<td>{{section.id}}</td>
<td>{{section.name}}</td>
</tr>
<tr ng-repeat-end="categories in navGetCategories">
<td>{{categories.id}}</td>
<td>{{categories.name}}</td>
</tr>
</tbody>
</table>
</div>
navController.js
var navApp = angular.module('navApp', ['ngResource']);
navApp.controller('menuController', ['$scope', 'navSectionList', 'navGetCategories',
function ($scope, navSectionList, navGetCategories) {
$scope.navSectionList = navSectionList.query();
$scope.getSectionID = function (event) {
var sectionID = event.currentTarget.attributes["data-id"].value;
$scope.sectionID = sectionID;
//console.log($scope.sectionID);
$scope.navGetCategories = navGetCategories.query(sectionID);
};
}
]);
navService.js
navApp.factory('navSectionList', [
'$resource', function ($resource) {
return $resource('/api/navigation/section/list', {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}
]);
navApp.factory('navGetCategories', [
'$resource', function ($resource) {
return $resource('/api/navigation/category/' + sectionID, {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}
]);
How do I get the value from the navController to the navService so that it can use that value to query the API? I feel like this should be something incredibly simple and basic but I'm either lacking sleep or lacking smarts at the moment. Please help.
You'll need to change your services to something like this:
navApp.factory('navGetCategories', ['$resource', function ($resource) {
var service = {
getResource: function(sectionID) {
return $resource('/api/navigation/category/' + sectionID, {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}
}
return service;
}]);
And then to use it will be something like this:
$scope.navGetCategories = navGetCategories
.getResource(sectionID)
.query();
navApp.factory('navGetCategories', [
'$resource', function ($resource) {
return {
getResouce:getResouce
}
function getResouce(sectionID) {
return $resource('/api/navigation/category/' + sectionID, {}, {
query: { method: 'GET', params: {}, isArray: true }
});
}
}
]);
Then call it like
navGetCategories.getResource(sectionID).query();

ng-repeat not populating table

I've populated a table with data from a JSON file, and then when the user clicks on an item in the table, a container and it's fields display below and it SHOULD be populated with that specific data, though it isn't.
The function select is invoked through list-patents.htm which contains the table with a list of patents. I have used the $rootScope object so the function is accessible from multiple controllers i.e. patentDetailsCtrl
Why isn't my table in patent-item.htm being populated with the ng-repeat directive?
var app = angular.module('myApp', ['ngRoute', 'angularMoment', 'ui.router', "chart.js"]);
$stateProvider
.state("patents.list.item", {
url: "/patent-item",
templateUrl: "templates/patents/list/patent-item.htm",
params: {
id: null,
appNo: null,
clientRef: null,
costToRenew: null,
renewalDueDate: null,
basketStatus: null,
costBandEnd: null,
nextStage: null
},
controller: "patentDetailsCtrl"
})
app.run(function($rootScope) {
$rootScope.select = function() {
return $rootScope.patentItem = item;
}
});
app.controller('patentDetailsCtrl', ['$scope', '$http', function($scope, $http) {
$scope.selectedPatent = $scope.patentItem;
console.log($scope.selectedPatent);
}]);
list-patents.htm
<tbody>
<tr ng-repeat="x in patents">
<td ng-click="select(x)"><a ui-sref="patents.list.item({id: x.id, appNo: x.applicationNumber, clientRef: x.clientRef, costToRenew: x.costToRenew, renewalDueDate: x.renewalDueDate, basketStatus: x.basketStatus, costBandEnd: x.costBandEnd, nextStage: x.nextStage})">{{x.applicationNumber}}</a></td>
<td ng-bind="x.clientRef"></td>
<td ng-bind="x.costToRenew">$</td>
<td ng-bind="x.renewalDueDate"></td>
<td><button type="button" class="btn btn-danger" ng-click="remove(x.id)">Remove</button></td>
</tr>
</tbody>
patent-item.htm
<table>
<tbody>
<thead>
<tr>
<td>applicationNumber</td>
<td>clientRef</td>
<td>costToRenew</td>
<td>renewalDueDate</td>
<td>basketStatus</td>
<td>costBandEnd</td>
<td>nextStage</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in selectedPatent">
<td>{{x.applicationNumber}}</td>
<td>{{x.clientRef}}</td>
<td>{{x.costToRenew}}</td>
<td>{{x.renewalDueDate}}</td>
<td>{{x.basketStatus}}</td>
<td>{{x.costBandEnd}}</td>
<td>{{x.nextStage}}</td>
</tr>
</tbody>
</table>
</tbody>
You need to correct below points:
your $rootScope.select() method doesn't accept data (select(x)) passed from your list-patents.htm, So put item as param.,
Like : $rootScope.select = function(item) { \\ code
Sine you do ng-repeat on $rootScope.patentItem, then I guess you need to make it array, and push the passed data to it. (No need of return statement as well.)
So run block should be like :
app.run(function($rootScope) {
rootScope.patentItem = [];
$rootScope.select = function(item) {
$rootScope.patentItem.push(item);
}
});
See this Example Fiddle
To me it seems like you're trying to access the patentItem selected in your select function. If you're passing it to the $rootScope this way, you will also need to access it this way.
So change the line as follows...
$scope.selectedPatent = $rootScope.patentItem

Angular Table Refresh

I have an api that gets called on page load. The data from the api is loaded into a table via angular ng-repeat. I also have a javascript function that gets called every 10 seconds that calls the same api for the same dataset. I Would like to know how i can apply the new dataset to the table and replace the old if the dataset changes and how to visually show this change with animation. The code is below.
Table code
<body ng-app="myApp" ng-controller="ScansController">
<div class="bs-example" id="scan-table">
<table id="scansTable" class="table table-striped">
<thead>
<tr>
<th>ScanId</th>
<th>First Name</th>
<th>Last Name</th>
<th>Time Stamp</th>
</tr>
<tr ng-repeat="scan in Scans">
<td>
{{scan.scanId}}
</td>
<td>
{{scan.firstName}}
</td>
<td>
{{scan.lastName}}
</td>
<td>
{{scan.timeStamp}}
</td>
</tr>
</thead>
</table>
</div>
Javascipt interval code
<script>
window.setInterval(function () {
$.ajax({
url: 'api/scans/',
type: 'Get',
dataType: 'json',
success: function (data) {
//Something here
},
error: function () {
alert("something failed");
}
});
}, 10000);
</script>
Angular Code
var myApp = angular.module('myApp', []);
myApp.service('dataService', function ($http) {
this.getData = function () {
return $http({
method: 'GET',
url: '/api/scans/'
});
}
});
myApp.controller('ScansController', function ($scope, dataService) {
$scope.Scans = [];
dataService.getData().then(function (result) {
$scope.Scans = result.data;
console.log(result.data);
});
});
You need to stay inside the current scope.
Setting an interval on a $http call is poison. Use a $timeout inside the success callback to recursively call the next interval.
myApp.controller('ScansController', function ($scope, $timeout, dataService) {
$scope.Scans = [];
function fetchData(){
dataService.getData().then(function (result) {
$scope.Scans = result.data;
$timeout(function(){ fetchData(); },10000);
});
}
fetchData();
});
As far as the table refresh that didnt get addressed, this is how i was able to make it work. I downloaded and applied the animate.css. I then gave the table a starting class to animate it on class load. I then have a function that fetches the array of data on page load and then another that fetches every .5 seconds and compares. If anything has changed, then the class is reapplied and it shows animation.
Angular Ng-Repeat Table
<link href="~/Content/animate.min.css" rel="stylesheet" />
<h1>
Scans
</h1>
<body ng-app="myApp" ng-controller="ScansController" >
<table id="scansTable" class="table table-striped">
<thead>
<tr>
<th>ScanId</th>
<th>First Name</th>
<th>Last Name</th>
<th>Time Stamp</th>
</tr>
<tr ng-repeat="scan in Scans" ng-class-odd="'odd'" ng-class-even="'even'" class="animated bounceIn">
<td>
{{scan.scanId}}
</td>
<td>
{{scan.firstName}}
</td>
<td>
{{scan.lastName}}
</td>
<td>
{{scan.timeStamp}}
</td>
</tr>
</thead>
</table>
Angular Controller
var myApp = angular.module('myApp', []);
myApp.service('dataService', function ($http) {
this.getData = function () {
return $http({
method: 'GET',
url: '/api/scans/'
});
}
});
myApp.controller('ScansController', function ($scope, dataService, $timeout) {
$scope.Scans = [];
$scope.NewScans = [];
function fetchData() {
dataService.getData().then(function (result) {
$scope.Scans = result.data;
$("#scansTable").removeClass('animated bounceIn');
});
}
function fetchNewData() {
dataService.getData().then(function (result) {
$scope.NewScans = result.data;
if ($scope.Scans.length != $scope.NewScans.length)
{
$("#scansTable").addClass('animated bounceIn');
$scope.Scans = $scope.NewScans
}
$timeout(function () { fetchNewData(); }, 500);
});
}
fetchData();
fetchNewData();
});

AngularJS: ng-repeat table doesn't update from UIB Modal

I have looked at a number of other questions related to this such as
AngularJS : ng-repeat list is not updated when a model element is spliced from the model array
ng-repeat not updating on update of array
However I think that the way I built my app is sufficiently different that these aren't helping me.
I think my idea of doing this:
$rootScope.$on('connectionDispositionChanged',function(event, item){
$scope.data.matches[item.index].info.disposition = item.disposition;
});
Isn't really working out the way I had hoped. I can actually see in the console that this updating, but it doesn't update in the table. Adding $scope.$apply() after this causes a digest in-progress error.
show.phtml
<div class="container-fluid" ng-app="analysisApp" ng-controller="analysisController">
<table class="table table-condensed">
<thead>
<tr >
<th ng-repeat="header in baseColumns" class="text-center">{{header.name | tableHeader}}</th>
<th ng-repeat="header in comparisonColumns" class="text-center text-info">{{header.name | tableHeader}}</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr table-row data="data" ng-repeat="item in data.matches | filter:searchMatchText track by $index">
</tbody>
</table>
<row class="col-md-12 text-center"><span class="text-muted">End of Data</span></row>
</div><!-- #matches -->
</div>
tableRowDirective.js
"use strict";
analysisApp.directive("tableRow", function($compile) {
var getTemplate = function(scope, element, attrs){
var base = scope.item.base;
var comp = scope.item.comparison;
var info = scope.item.info;
// other non-relevant code...
returnString += '<td class="text-center"><button class="btn btn-default btn-xs" ng-click="matchesSetDisposition(item, data.settings, $index)" >Set Disposition</button>';
returnString += '</td>';
return returnString;
};
var linker = function(scope, element, attrs){
element.html(getTemplate(scope, element, attrs));
$compile(element.contents())(scope);
};
return {
restrict : "A",
replace : true,
link: linker
};
});
analysisController.js
"use strict";
analysisApp.controller('analysisController', ['$scope','$rootScope','loadData','saveData','$uibModal', function ($scope, $rootScope, loadData, saveData, $uibModal, $log) {
$rootScope.$on('connectionDispositionChanged',function(event, item){
// $scope.data.matches[item.index].info.disposition = item.disposition;
});
$scope.matchesSetDisposition = function(item, scope, index){
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: '/angular/analysis/templates/matches-modal.html',
controller: 'matchesModalController',
size: 'lg',
resolve: {
itemData: function () {
return {
dispositionLabels: $scope.dispositionLabels,
disposition: item.info.disposition,
connectionID: item.info.id,
comparisonID: comparisonID,
baseItemID: item.base.id,
baseTitle: itemTitle(item.base),
comparisonItemID: item.comparison.id,
comparisonTitle: itemTitle(item.comparison),
index: index
}
}
}
});
modalInstance.result.then(function (item) {
$scope.data.matches[item.index].info.disposition = item.disposition;
saveTheData('/analysis/apisaveconnectiondisposition', item);
}, function () {
});
};
}]);
matchesModalController.js
"use strict";
analysisApp.controller('matchesModalController', function ($scope, $rootScope, $uibModalInstance, itemData, saveData) {
$scope.itemData = itemData;
$scope.ok = function (item) {
$uibModalInstance.close(item);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.delink = function (item) {
BootstrapDialog.confirm({
title: 'WARNING',
message: '<p>Are you sure that you want to break the link between these items?</p>',
type: BootstrapDialog.TYPE_DANGER,
btnOKLabel: 'Break the link',
btnOKClass: 'btn-danger',
callback: function(result) {
if(result) {
$uibModalInstance.dismiss('delink');
saveTheData('/analysis/apidelink', item);
}else {
// cancel the operation
}
}
});
};
var saveTheData = function(url, item){
saveData
.postData(url, item)
.then(function(dataResponse){
$rootScope.$broadcast('connectionDispositionChanged', item);
})
};
});

AngularJS : directive does not update scope after $http response in parent scope

I got this directive:
.directive('studentTable', [function() {
return {
restrict: 'A',
replace: true,
scope: {
students: "=",
collapsedTableRows: "="
},
templateUrl: 'partials/studentTable.html',
link: function(scope, elem, attrs) {
...
}
}
}
Template:
<table class="table">
<thead>
<tr>
<th><b>Name</b></th>
<th><b>Surname</b></th>
<th><b>Group</b></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="student in students track by $index">
<td>{{ student.name }}</td>
<td>{{ student.surname }}</td>
<td>{{ student.group }}</td>
</tr>
</tbody>
</table>
Use directive in my html like this:
<div student-table students="students"
collapsedTableRows="collapsedTableRows"></div>
And the parent controller:
.controller('SchoolController', ['$scope', 'User', function($scope, User){
$scope.students = [];
$scope.collapsedTableRows = [];
$scope.search = function(value) {
if(value) {
var orgId = $state.params.id;
var search = User.searchByOrg(orgId, value);
search.success(function (data) {
$scope.students = data;
$scope.collapsedTableRows = [];
_(data).forEach(function () {
$scope.collapsedTableRows.push(true);
});
});
}
}
}])
Now at the beginnig, the table is empty, because no users in students array. After I click search, and get list of students object, I put them to scope variable, but the directive does not update, neither it find change in model (scope.$watch('students',...). What am I missing?
P.S. If I simulate the data using $httpBackend, directive works as it should.
Please make sure that data object returning array of student because somtimes you have to use data.data that simple demo should helps you:
http://plnkr.co/edit/UMHfzD4oSCv27PnD6Y6v?p=preview
$http.get('studen.json').then(function(students) {
$scope.students = students.data; //<-students.data here
},
function(msg) {
console.log(msg)
})
You should try changing the controller this way
...
$scope.$apply(function() {
$scope.students = data;
})
...
This will start a digest loop, if it's not already in progress.
Another form that will do almost the same thing is this:
...
$scope.students = data;
$scope.$digest()
...
PS:
The first method is just a wrapper that execute a $rootScope.$digest() after evaluating the function, considering that a $digest evaluates the current scope and all it's children calling it on the $rootScope is pretty heavy.
So the second method should be preferred if it works.

Categories