react in large angular app - angularjs

So I have an angular large web application, I have a few pages where I would want to use React to decrease the rendering time of the page (>3000 rows).
While searching for a solution I encountered this react plugin: http://bebraw.github.io/reactabular/
It lets me add a datatable with editing options and more - perfect for my needs.
The problem is that there is no easy way to include it in a prebuilt angular app.
Since I am new to react, I wanted to integrate it to my current angular application and I am searching the web for answers for a long time.
I am looking for a way to integrate it to my current code, that looks like the following:
var contactManager = angular.module('contactManager', ['ngMaterial','ngCookies','ngRoute','loadingOnAJAX','truncate','chart.js','xeditable','datatables','ui.calendar',
'xeditable', 'ngFileUpload','ui.bootstrap','html5.placeholder','luegg.directives','angular-svg-round-progress','ngProgress','ngDraggable'])
.run(function($rootScope, ngProgress, editableOptions) {
editableOptions.theme = 'bs3'; // bootstrap3 theme. Can be also 'bs2', 'default'
$rootScope.$on('$routeChangeStart', function(ev,data) {
ngProgress.start();
});
$rootScope.$on('$routeChangeSuccess', function(ev,data) {
// Close menu on mobiles
ngProgress.complete();
});
})
.config(['$routeProvider','$locationProvider','$controllerProvider', function($routeProvider, $locationProvider, $controllerProvider) {
$routeProvider
.when('/index', {
templateUrl: 'partials/adminPanel.html',
controller: 'GroupPanelCtrl',
resolve: GroupPanelCtrl.resolve
})
.when('/profile/:id', {
templateUrl: 'partials/profile.html',
controller: 'ProfileCtrl'
})
.when('/group/:id/contacts', {
templateUrl: 'partials/group-contacts.html',
controller: 'GroupContactsInfo',
resolve: GroupContactsInfo.resolve
})
and the HTML:
<table datatable="ng" class="table table-bordered table-hover dataTable" role="grid" data-page-length="100">
<thead>
<tr>
<th></th>
<th>{{texts.fullName}}</th>
<th>{{texts.title}}</th>
<th>{{texts.mobile}}</th>
<th>{{texts.email}}</th>
<th>{{texts.department}}</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="contact in contacts track by $index">
<td ng-click="clickContact()">{{img}}
</td>
<td ng-click="clickContact()">
<a>
{{::contact.fullName}}
</a>
</td>
<td ng-click="clickContact()">
{{::contact.title|| ""}}
</td>
<td ng-click="clickContact()">
{{::contact.phoneFormatted}}
</td>
<td ng-click="clickContact()">
<span>
{{::contact.email}}
</span>
</td>
<td ng-click="clickContact()">
{{::contact.department|| ""}}
</td>
<td ng-click="deleteContact(contact, $index)">
<span class="fa fa-trash showOnhover"></span>
</td>
</tr>
</tbody></table>

If you need to call Angular code from a non-Angular app, try https://github.com/bcherny/ngimport. It lets you require/import Angular services from non-Angular files.

Related

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 > router resolve > "Error: [$injector:unpr] Unknown provider"

im trying to add a resolve\promise to my project, so when you ask for a page it will load up only after receiving the json from the server.
this is my js code:
'use strict';
angular.module('myApp.show', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/show', {
templateUrl: 'views/show.html',
controller: 'showCtrl',
resolve: {
booksList: function ($http) {
return ($http.get('data/books.json')
.then(function (data) {
return data;
}));
}
}
});
}])
.controller('showCtrl', ['booksList', '$scope', function (booksList, $scope) {
$scope.books = booksList;
$scope.removeRow = function (productIndex) {
if (window.confirm("are you sure?")) {
$scope.books.splice(productIndex, 1);
}
}
}])
but this is what i get:
Error: [$injector:unpr] Unknown provider: booksListProvider <- booksList <- showCtrl
i kinda new to angular, but i followed several tutorials and while it worked in the video - i keep getting errors.
html:
<div class="table-responsive">
<div ng-app="myApp.show" ng-controller="showCtrl"> <!-- ctrl -->
<table st-table="rowCollection" class="table table-striped">
<thead>
<tr>
<th st-sort="author">Author</th>
<th st-sort="date">Date</th>
<th st-sort="title">Title</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="(bookIndex, book) in books">
<td class="col-md-3">{{ book.author }}</td>
<td class="col-md-3">{{ book.date | date }}</td>
<td class="col-md-4">{{ book.title | beutifyTitle }}</td>
<td class="col-md-1"><ng-include src="'views/partials/editBook.html'"></ng-include></td>
<td class="col-md-1"><button class="btn btn-warning" ng-click="removeRow()">Delete</button></td>
</tr>
</tbody>
</table>
</div>
</div
You should be removing ng-controller="showCtrl" from your template. The reason being is, you are assing showCtrl via router already. But as you are again wrote ng-controller over inline template in that case it fails to get booksList resolve provider.

AngularJS - how to export content of a directive (ng-repeat) into separate downloadable HTML file

As in the title: How can I use Angular to allow downloading the content of a div (ng-repeat directive in this case) in a new HTML file?
I have tried this:
var content = 'file content';
var blob = new Blob([ content ], { type : 'text/html' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );
HTML:
<a download="content.txt" ng-href="{{ url }}">download</a>
but it doesn't work.
I know how to achieve in pure JS, but I was wondering if there is some shorter, handy way of achieving that with Angular?
You can use FileSaver.js to output the table as a file for the user to download.
<div id="exportable">
<table width="100%">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>DoB</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items">
<td>{{item.name}}</td>
<td>{{item.email}}</td>
<td>{{item.dob | date:'MM/dd/yy'}}</td>
</tr>
</tbody>
</table>
</div>
Export call:
var blob = new Blob([document.getElementById('exportable').innerHTML], {
type: "text/html;charset=utf-8"
});
saveAs(blob, "content.html");
};
Demo: http://jsfiddle.net/XNVj3/108/

Angular - update a table cell

In my angular app I have the typical table iterator via ng-repeat.
<table class="table table-bordered table-condensed table-striped">
<tbody>
<tr ng-repeat="assay in assays">
<td>
<td key="id">{{assay.id}}</td>
<td key="source">{{assay.source}}</td>
<td key="status">{{assay.status}}</td>
... etc ...
</tr>
</tbody>
</table>
Using a button a user makes a change in a modal dialog. This is all working fine, but I want to update a single cell in the table with the result; I have the id of the row as well as the field name.
Is it possible in Angular to access a single cell, or even update the entire row, if you have the index? At present I am achieving this by refreshing all the table data from the remote server... not ideal.
I guess I am looking for something like:
assays[id].status = 'great!'
Thanks to #JB Nizet 's comment above I was able to achieve what I wanted, I think in a more angular way than I had been headed in. The angular data binding is really a great way to handle model/view updates.
I have posted a simplified version of the code here in case it helps someone else. I am using AngularUI for the modals.
view
<table class="table table-bordered table-condensed table-striped">
<tbody>
<tr ng-repeat="assay in assays">
<td>
<td key="id">{{assay.id}}</td>
<td key="source">{{assay.source}}</td>
<td key="status">{{assay.status}}</td>
... etc ...
<button class="btn btn-xs btn-info" ng-click="invokeAssayModal(assay)">Assay</button>
</tr>
</tbody>
</table>
controller
$scope.invokeAssayModal = (assay) ->
$scope.assay = assay
modalInstance = $modal.open(
templateUrl: '/templates/panel/assay_modal.html'
controller: 'AssayModalCtrl'
windowClass: 'configure-dialog'
resolve:
assay: ->
$scope.assay
)
modalInstance.result.then ((assay) ->
$scope.assay = assay
return
), ->
return
modal view
<button type="button" class="btn btn-mini btn-success" ng-click="select(primerPair)">Select</button>
modal controller
$scope.select = (primerPair) ->
$scope.assay.source = primerPair.source

ng-repeat only working in IE

I'm busy trying to learn Angular+Typescript+bootstrap and I've stumbled on a problem I can't solve. I have an ng-repeat in a partial view:
<div>
<h1>Debtors</h1>
<table class="table table-hover">
<thead>
<tr>
<th>Debtor</th>
<th class="col-sm-1"></th>
<th class="col-sm-1"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="Debtor in vm.Debtors">
<td>{{Debtor.Name}}</td>
<td><i class="fa fa-pencil fa-fw fa-lg"></i> Edit</td>
<td><i class="fa fa-trash-o fa-fw fa-lg"></i> Delete</td>
</tr>
</tbody>
</table>
With a controller:
module Controllers
{
export class DebtorsController
{
static $inject = ['$scope', '$http', '$modal'];
Debtors: Models.TradePartyModel[];
ModalService: ng.ui.bootstrap.IModalService;
constructor($scope: ng.IScope, $http: ng.IHttpService, $modal: ng.ui.bootstrap.IModalService)
{
this.ModalService = $modal;
$http.get("http://localhost:51263/api/debtors/1")
.success((Debtors) =>
{
this.Debtors = Debtors;
});
}
}
}
The routing is defined as:
$routeProvider.when('/Debtors/ViewDebtors', {
templateUrl: 'App/Views/Debtors/Debtors.html',
controller: Controllers.DebtorsController,
controllerAs: "vm"
});
This works perfectly fine in IE, it displays the table and all of the data, but this doesn't seem to work in FireFox or Chrome. I've checked fiddler and the data is coming back correctly, but the table is not showing.
Any Ideas? Thanks!
Okay, so after a few attempts at fixing this, Chrome decided to spit this error out:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
Enabling CORS on the server fixed my issue. Looks like its working on all browsers now.

Resources