Breaking the ice on angular - angularjs

I have a view:
<div id="productList" ng-controller="ProductController">
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th width="100%">Item ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in productItems">
<td><input type="checkbox" /></td>
<td><% item.ID %></td>
</tr>
</tbody>
</table>
</div>
I have a controller:
var TestApp = angular.module('TestApp', [], function( $interpolateProvider ) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
});
TestApp.controller('ProductController', function ( $scope ) {
ProductRepository.GetPaginatedProducts();
$scope.productItems = ProductRepository.Model;
});
I have a model:
var ProductRepository = function ()
{
return {
Model: null,
GetPaginatedProducts: function()
{
$.ajax( {
"url": Test.URL + "/json/products/paginated",
"dataType": "json",
"method": "post",
"success": function( data )
{
ProductRepository.Model = data;
}
} );
},
}
}
When the ajax finished, it updates the ProductRepository.Model data variable which I want the angular controller scope.productItems to work off of.
This is my first time using angular and i think I've missed the point,
Why is the table not updating with the information?

Please see here for sample ajax call http://plnkr.co/edit/5CiC8MWiwo010nwu32he?p=preview
var TestApp = angular.module('plunker', []);
TestApp.factory('ProductRepository', function($http, $q) {
var Model = [];
return {
Model: Model,
GetPaginatedProducts: function() {
var deferred = $q.defer();
$http.get('paginated.json').then(
//sucess
function(result) {
deferred.resolve(result.data)
}
//error
, function() {});
return deferred.promise;
}
};
})
TestApp.controller('ProductController', function($scope, ProductRepository) {
$scope.productItems = [];
ProductRepository.GetPaginatedProducts().then(function(data) {
//success
$scope.productItems = data;
},
//error
function() {
alert("can't get data");
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="plunker">
<div id="productList" ng-controller="ProductController">
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th width="100%">Item ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in productItems">
<td>
<input type="checkbox" />
</td>
<td>{{item.ID}}</td>
</tr>
</tbody>
</table>
</div>
</div>

As #tymeJV stated in the comment, you should use $http or $resource (or restangular) to perform the data access. If you are updating values outside of Angular's framework, it has no way of knowing that data has changed. By using something like $http or $resource, Angular is aware when an event has occurred that can change values and automatically checks for updates.
Alternately, you could manually issues an $apply() to essentially handle the digest update manually (ensuring Angular goes through a digest cycle when you've changed values outside of Angular). Take a look at the documentation on $apply() at https://docs.angularjs.org/api/ng/type/$rootScope.Scope .
However, your best bet is to use the Angular approach in the first place and avoid using jQuery for this.

Related

angularjs with jquery datatable search button does not work

When i try to use jquery datatable with angularjs, search button doesn't work. but if i bind the data using pure asp.net mvc by actionresult method, it works correctly.
I bind the data from user-role.js and i'm using ng-repeat to bind them.
my angularjs version is 1.7.2
jquery.datatble version is 1.10.16
cshtml codes below.
<script type="text/javascript" src="~/Scripts/app/user-role.js"></script>
<div ng-app="userRoleGridApp" ng-controller="userRoleGridCtrl">
<div class="container">
<div class="row">
<div class="col-md-9">
<div class="table-responsive">
<table class="table" id="user-table">
<thead>
<tr>
<th>UserId</th>
<th>User Name</th>
<th>Name Surname</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in Users track by $index">
<th>{{x.UserId}}</th>
<th>{{x.UserName}}</th>
<th>{{x.NameSurname}}</th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#user-table').DataTable({
dom: 'Bfrtip',
buttons: [
'copyHtml5',
'excelHtml5',
//'csvHtml5',
'pdfHtml5'
]
});
});
</script>
Js side
var app = angular.module("userRoleGridApp",
['ngAnimate',
'ngRoute']);
var baseUrl = location.origin;
app.controller("userRoleGridCtrl", function ($scope, $http) {
$scope.Users = {};
initUser();
function initUser() {
$http({
method: "get",
url: baseUrl + "/UserRole/GetUsers"
}).then(function (response) {
$scope.Users = response.data;
}, function () {
alert("Error Occur");
});
}
});

Using AngularJS, how can I match items from two separate ng-repeats?

Using AngularJS, I am creating a table that is pulling data with two web requests.
Each web request has it's own ng-repeat in the HTML, ng-repeat="user in users" and ng-repeat="app in apps". Right now all existing apps are showing in every repeat of user. What I'd like to do is some kind of match, lookup, or filter and only show apps that the user is associated with. So, when user.Title == app.Title.
Here is the HTML:
<div ng-app="myApp">
<div ng-controller="MainCtrl">
<div class="ProfileSheet" ng-repeat="user in users">
<h3 class="heading">User Profile</h3>
<table id="Profile">
<tr>
<th>User</th>
<td>{{user.Title}}</td>
</tr>
<tr>
<th>First Name</th>
<td>{{user.FirstName}}</td>
</tr>
<tr>
<th>Last Name</th>
<td>{{user.LastName}}</td>
</tr>
<tr>
<th>Job Title</th>
<td>{{user.JobTitle}}</td>
</tr>
<tr>
<th>Emp ID</th>
<td>{{user.EmployeeID}}</td>
</tr>
<tr>
<th>Officer Code</th>
<td>{{user.OfficerCode}}</td>
</tr>
<tr>
<th>Email</th>
<td>{{user.Email}}</td>
</tr>
<tr>
<th>Telephone</th>
<td>{{user.WorkPhone}}</td>
</tr>
<tr>
<th>Fax Number</th>
<td>{{user.WorkFax}}</td>
</tr>
<tr>
<th>Location Description</th>
<td>{{user.LocationDescription}}</td>
</tr>
<tr>
<th>Mailstop / Banking Center #</th>
<td>{{user.Mailstop}}</td>
</tr>
</table>
<br>
<h3 class="heading">User Applications</h3>
<div style="border:3px solid #707070; padding-right:12px;">
<h4 style="padding-left:5px;">User Applications</h4>
<table id="Apps">
<tr id="AppsHeading">
<th>Application</th>
<th>User ID</th>
<th>Initial Password</th>
<th>Options / Comment</th>
<th>Setup Status</th>
</tr>
<tr ng-repeat="app in apps">
<td>{{app.Application.Title}}</td>
<td>{{app.Title}}</td>
<td>{{app.InitialPassword}}</td>
<td>{{app.OptionsComments}}</td>
<td style="border-right:3px solid #707070;">{{app.SetupStatus}}</td>
</tr>
</table>
</div>
</div>
</div>
The JS:
var app = angular.module('myApp', ['ngSanitize']);
var basePath = "https://portal.oldnational.com/corporate/projecthub/anchormn/associates"
app.controller('MainCtrl', function($scope, $http, $q){
var supportList;
$(document).ready(function() {
$scope.getAdminList();
$scope.getAppsList();
});
// $scope.selectedIdx = -1;
// $scope.showResults = false
$scope.prepContext = function(url,listname,query){
var path = url + "/_api/web/lists/getbytitle('" + listname + "')/items" + query;
console.log(path);
return path;
}
$scope.getAdminList = function() {
supportList = $http({
method: 'GET',
url: this.prepContext(siteOrigin+"/corporate/projecthub/anchormn/associates","User Administration","?$orderBy=LastName"),
headers: {
"Accept": "application/json; odata=verbose"
}
}).then(function(data) {
//$("#articleSection").fadeIn(2000);
console.log("adminlist", data.data.d.results);
$scope.users = data.data.d.results;
});
};
$scope.getAppsList = function() {
supportList = $http({
method: 'GET',
// url: this.prepContext(siteOrigin+"/corporate/projecthub/anchormn/associates","User Applications","?$select=Title,InitialPassword,OptionsComments,SetupStatus,Application/Title&$orderBy=Application&$expand=Application"),
url: this.prepContext(siteOrigin+"/corporate/projecthub/anchormn/associates","User Applications","?$select=Title,InitialPassword,OptionsComments,SetupStatus,Application/Title,ParentUserLink/ID&$orderBy=Application&$expand=Application,ParentUserLink"),
headers: {
"Accept": "application/json; odata=verbose"
}
}).then(function(data) {
//$("#articleSection").fadeIn(2000);
console.log("appslist", data.data.d.results);
$scope.apps = data.data.d.results;
});
};
});
app.config([
'$compileProvider',
function($compileProvider){
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrom-extension|javascript):/)
}
]);
How can I do this?
There's a lot of extraneous code in the controller. For the purposes of this answer I removed it. Also I understand that users and apps are related by a property called Title but the name was confusing me - forgive me if the data doesn't make sense.
Suggestion: Only use $(jQuery) where strictly necessary. Angular provides a lot of functionality that replaces jQuery functionality. Instead of using $.ready like:
$(document).ready(function() {
$scope.getAdminList();
$scope.getAppsList();
});
wait to bootstrap your application until the document is ready using the following code:
(function () {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
})();
Then you don't have to burden the controller with the responsibility of waiting until the document is loaded. Note: ng-app was removed from the markup.
Suggestion: Use $q.all() to wait for multiple promises to resolve. $q.all() will wait until all promises resolve to call .then(). This helps to ensure that all data is available when you start to use it.
Suggestion: Only assign properties and functions to $scope if they will be used by the view. I removed the functions that are not used by the view from the $scope object.
How does it work?
When the controller loads, we use $q.all() to invoke and wait for fetchAdminList() and fetchAppsList() to fetch data from an API. Once each API request resolves we unwrap the data in .then() callbacks and return it (read more on promise chaining to understand what happens when a value is returned from .then()). When both promises resolve, we store the data on $scope to make it available to the view. We also pre-compute which applications each user can use and store that data in $scope.userApps to make it available to the view.
I did not have access to the APIs you are fetching data from. I substituted $http calls with an immediately resolving promise using $q.resolve() and static data. When you are ready just replace $q.resolve(...) with the original $http(...) calls in the fetch functions.
Run the snippet to see it in action.
var app = angular.module('myApp', []);
(function () {
angular.element(document).ready(function () {
angular.bootstrap(document, ['myApp']);
});
})();
var USERS = [
{
Title: 'Software Engineer',
FirstName: 'C',
LastName: 'Foster',
EmployeeID: 1
},
{
Title: 'Software Engineer',
FirstName: 'J',
LastName: 'Hawkins',
EmployeeID: 2
},
{
Title: 'CEO',
FirstName: 'Somebody',
LastName: 'Else',
EmployeeID: 3
}
];
var APPS = [
{
Application: { Title: 'StackOverflow' },
Title: 'Software Engineer'
},
{
Application: { Title: 'Chrome' },
Title: 'Software Engineer'
},
{
Application: { Title: 'QuickBooks' },
Title: 'CEO'
}
]
app.controller('MainCtrl', function ($scope, $http, $q) {
$q.all({
users: fetchAdminList(),
apps: fetchAppsList()
})
.then(function(result) {
// Store results on $scope
$scope.users = result.users;
$scope.apps = result.apps;
// Pre-compute user apps
$scope.userApps = $scope.users.reduce(
function(userApps, user) {
userApps[user.EmployeeID] = getUserApps(user.Title);
return userApps;
},
[]
);
});
function fetchAdminList() {
return $q.resolve({ data: { d: { results: USERS } } })
.then(function (data) { return data.data.d.results; });
}
function fetchAppsList() {
return $q.resolve({ data: { d: { results: APPS } } })
.then(function (data) { return data.data.d.results; });
}
// Get a list of apps that apply to user title
function getUserApps(userTitle) {
return $scope.apps.filter(function(app) {
return app.Title === userTitle;
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
<div>
<div ng-controller="MainCtrl">
<div class="ProfileSheet" ng-repeat="user in users">
<h3 class="heading">User Profile</h3>
<table id="Profile">
<tr>
<th>User</th>
<td>{{user.Title}}</td>
</tr>
<tr>
<th>First Name</th>
<td>{{user.FirstName}}</td>
</tr>
<tr>
<th>Last Name</th>
<td>{{user.LastName}}</td>
</tr>
<tr>
<th>Job Title</th>
<td>{{user.JobTitle}}</td>
</tr>
<tr>
<th>Emp ID</th>
<td>{{user.EmployeeID}}</td>
</tr>
<tr>
<th>Officer Code</th>
<td>{{user.OfficerCode}}</td>
</tr>
<tr>
<th>Email</th>
<td>{{user.Email}}</td>
</tr>
<tr>
<th>Telephone</th>
<td>{{user.WorkPhone}}</td>
</tr>
<tr>
<th>Fax Number</th>
<td>{{user.WorkFax}}</td>
</tr>
<tr>
<th>Location Description</th>
<td>{{user.LocationDescription}}</td>
</tr>
<tr>
<th>Mailstop / Banking Center #</th>
<td>{{user.Mailstop}}</td>
</tr>
</table>
<br>
<h3 class="heading">User Applications</h3>
<div style="border:3px solid #707070; padding-right:12px;">
<h4 style="padding-left:5px;">User Applications</h4>
<table id="Apps">
<tr id="AppsHeading">
<th>Application</th>
<th>User ID</th>
<th>Initial Password</th>
<th>Options / Comment</th>
<th>Setup Status</th>
</tr>
<tr ng-repeat="app in userApps[user.EmployeeID]">
<td>{{app.Application.Title}}</td>
<td>{{app.Title}}</td>
<td>{{app.InitialPassword}}</td>
<td>{{app.OptionsComments}}</td>
<td style="border-right:3px solid #707070;">{{app.SetupStatus}}</td>
</tr>
</table>
</div>
</div>
</div>

AngularJS - DOM not updated in Chrome and Mozilla

I'm using angularjs and angular-ui-bootstrap to make a table and page some data, which I get from an API.
I've made sure my service receives the correct data and that the requests are built properly. My pageChange function is properly triggered and my first page gets loaded successfully.
So I have the following controller setup:
(function () {
'use strict';
initContacts.$inject = ['$scope', '$http'];
angular.module('app')
.config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
angular.module('app', ['ui.bootstrap']).controller('contactSearchController', initContacts);
function initContacts($scope, $http) {
$scope.contacts = [];
$scope.totalItems = 0;
$scope.pages = 0;
$scope.currentPage = 0;
$scope.maxPageLinksShown = 5;
if (window.location.hash !== '') {
$scope.currentPage = window.location.hash.replace('#', '');
}
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1)).success(function (data) {
$scope.contacts = data.Contacts;
$scope.totalItems = data.Count;
$scope.PageSize = data.Contacts.length;
$scope.pages = Math.ceil((data.Count / $scope.PageSize));
});
$scope.pageChanged = function () {
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1))
.success(function (data) {
$scope.contacts = data.Contacts;
});
};
}
}
})();
And in my view I have:
<div ng-app="app" ng-controller="contactSearchController">
<table class="table table-striped table-hover contact-search-table">
<thead>
<tr>
<td class="contact-title">
Titel
</td>
<td class="department">
Afdeling
</td>
<td class="name">
Navn
</td>
<td class="work-phone">
Telefon
</td>
<td class="mobile">
Mobile
</td>
<td class="email">
Email
</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="(key, value) in contacts">
<td class="contact-title">{{value.Title}}</td>
<td class="department">{{value.Department}}</td>
<td class="name">{{value.FullName}}</td>
<td class="work-phone">{{value.WorkPhone}}</td>
<td class="mobile">{{value.WorkMobile}}</td>
<td class="email">{{value.Email}}</td>
</tr>
</tbody>
</table>
<div class="col-xs-12 pager-container">
<ul uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()" max-size="maxPageLinksShown" class="pagination-sm" boundary-links="true" num-pages="pages"></ul>
</div>
Now this works to some extent.
My problem
When I click the page links, the pageChanged() function is called, I get the data from my api and it's all correct, the list in the scope appears to be updated fine, but the table in my view doesn't change.
This solution works fine ONLY in IE ! (who would have thought huh...)
No exceptions get thrown.
I swear this was working yesterday.
Any help is much appreciated!
EDIT
What I've tried:
-Putting the assignment of the contacts in an $apply like so:
$scope.pageChanged = function () {
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1))
.success(function (data) {
$scope.$apply(function () { // apply
$scope.contacts = data.Contacts;
});
});
};
I got a "$digest already in progress" error from this.
tried to wrap the apply in a timeout like so:
$timeout(function(){ //any code in here will automatically have an apply run afterwards });
Got rid of the error but the DOM still won't update.
I had such a problem and I solved it using $scope.$apply:
$scope.pageChanged = function () {
$http.get("/api/ContactApi/GetPage?pageIndex=" + ($scope.currentPage - 1))
.success(function (data) {
$scope.$apply(function () { // apply
$scope.contacts = data.Contacts;
});
});
};
You should use $scope.$applyAsync instead. It will be applied to the next digest cycle.
If you need to target all the $scopes of your AngularJS application, use $rootScope.$applyAsync instead.
Official doc
Okay so what worked in this situation was changing the way I'm visualizing the ng-repeat like so:
<tr ng-repeat="contact in getContacts()">
<td class="contact-title">{{contact.Title}}</td>
<td class="department">{{contact.Department}}</td>
<td class="name">{{contact.FullName}}</td>
<td class="work-phone">{{contact.WorkPhone}}</td>
<td class="mobile">{{contact.WorkMobile}}</td>
<td class="email">{{contact.Email}}</td>
</tr>
And heres the getContacts() function:
$scope.getContacts = function () {
var data = $scope.contacts;
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
I don't really know why that works, but it does.

bootstrap-table not rendering upon updating model in Angular

Hi I am not able to render table using bootstrap-table and angular. here is my code, I think I need to call bootstrap-table init method in angular ajax call. Can some one guide me on how to do this..?
angular
.module('reports')
.controller(
'ReportCtrl',
[
'$scope',
'$http',
'ngProgress',
function($scope, $http, ngProgress) {
var vm = this;
vm.mdp = {};
vm.mdp.data = [];
vm.mdp.columns = [];
$scope.submit = function() {
var report = $scope.tab;
$http.post('/reports/cmd/getData', {
report : report,
date : createdAfter
}).success(function(data) {
vm.mdp.data = data;
$.each(data[0], function(key, value){
vm.mdp.columns.push(key);
});
}).error(function(error) {
alert(error);
});
};
} ]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="mdp" class="panel" ng-controller="ReportCtrl as report" ng-show="panel.isSelected('mdp')">
<table data-toggle="table" data-show-columns="true" data-search="true" data-show-export="true" data-pagination="true" data-height="299">
<thead>
<tr>
<th ng-repeat="c in report.mdp.columns" data-field= {{c}} >{{ c }}</th>
</tr>
</thead>
<tr ng-repeat="r in report.mdp.data">
<td ng-repeat="c in report.mdp.columns">{{ r[c] }}</td>
</tr>
</table>
</div>
Integrating Bootstrap Table with Angular is solved here:
https://github.com/wenzhixin/bootstrap-table/issues/165
https://github.com/AkramKamal/bootstrap-table-examples/tree/master/integrate
I have some minor changes in my implementation of this solution which I will upload to Github / JSFiddle shortly. But the links above will allow you to get going.

show 2 array through ng-repeat error

i am trying to make a table from 2 arrays in AngularJS one array contains Employees name and other array contain Services Name and rest of the cells contain check boxes but when i do i got error Error: [ngRepeat:dupes]
here is my AngularJS code
var model = angular.module('wizard', ['ngRoute'])
.config(["$routeProvider", function ($routeProvider) {
$routeProvider.when("/m5", {
controller: "model5Controller",
templateUrl: "/templates/m5.html"
});
$routeProvider.otherwise({ redirectTo: '/' });
}]);
var model5Controller = ["$scope", "$http", "$window", function ($scope,
$http, $window) {
$scope.PBA = [];
$scope.SOB = [];
$http.get('/Test/GetPBA').
then(
function (result) {
//success
$scope.PBA = result.data;
},
function () {
//error
});
$http.get('/Test/GetSOB').
then(
function (result) {
//success
$scope.SOB = result.data;
},
function () {
//error
});
$scope.save = function () {
//to be written
};
}];
GetPBA/GetSOB returns JSON type Array of List
both contain properties like Id, Name
here is my html
<div>
<form ng-submit="save()">
<table border="1">
<tr>
<td><strong>Services</strong></td>
<td ng-repeat="e in PBA">{{e.Name}}</td>
</td>
<tr ng-repeat="i in SOB">
<td>{{i.Name}}</td>
<td ng-repeat="e in PBA">
<input type="checkbox" name="{{e.Id}}" />
</td>
</tr>
</table>
</form>
Try this
<tr ng-repeat="i in SOB track by $index">
<td>{{i.Name}}</td>
<td ng-repeat="e in PBA track by $index">
<input type="checkbox" name="{{e.Id}}" />
</td>
</tr>

Resources