How to download xls file with Angular and Spring Rest Service? - angularjs

I created a factory in angular to call a spring rest service to download xls file.
this is my factory:
angular.module('app')
.factory('SportsbookServiceCustom', function ($http) {
return {
exportToXLS: function () {
return $http.get('api/sportsbooks/downloadXLS').then(function (response) {
return response.data;
});
}
};
});
I have this code for my rest service:
#RequestMapping(value = "/sportsbooks/downloadXLS")
public void downloadXLS(HttpServletResponse response) {
Pageable pageable = new PageRequest(0, 20, Direction.ASC, "id");
Page<Sportsbook> page = sportsbookRepositoryCustom.findAll(pageable, null, null, null);
List<Sportsbook> sportsbookList = page.getContent();
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Sample sheet");
Map<String, Object[]> data = new HashMap<String, Object[]>();
data.put("1", new Object[] { "Emp No.", "Name", "Salary" });
data.put("2", new Object[] { 1d, "John", 1500000d });
data.put("3", new Object[] { 2d, "Sam", 800000d });
data.put("4", new Object[] { 3d, "Dean", 700000d });
Set<String> keyset = data.keySet();
int rownum = 0;
for (String key : keyset) {
Row row = sheet.createRow(rownum++);
Object[] objArr = data.get(key);
int cellnum = 0;
for (Object obj : objArr) {
Cell cell = row.createCell(cellnum++);
if (obj instanceof Date)
cell.setCellValue((Date) obj);
else if (obj instanceof Boolean)
cell.setCellValue((Boolean) obj);
else if (obj instanceof String)
cell.setCellValue((String) obj);
else if (obj instanceof Double)
cell.setCellValue((Double) obj);
}
}
if (workbook != null) {
// Writing file to outputstream
try {
InputStream fileInputStream = new ByteArrayInputStream(workbook.getBytes());
OutputStream output = response.getOutputStream();
response.reset();
response.setContentLength((int) (workbook.getBytes().length));
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment;filename=yourFileName.xls");
IOUtils.copyLarge(fileInputStream, output);
output.flush();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
The code runs without error but downloading file isn't working. What is error?

Try with filesaver.js
Pass the Json from your Server side (here Spring)
<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>
in your angular controller you received items from server eg.below
$scope.items = [{
name: "John Smith",
email: "j.smith#example.com",
dob: "1985-10-10"
}, {
name: "Jane Smith",
email: "jane.smith#example.com",
dob: "1988-12-22"
}, {
name: "Jan Smith",
email: "jan.smith#example.com",
dob: "2010-01-02"
}, {
name: "Jake Smith",
email: "jake.smith#exmaple.com",
dob: "2009-03-21"
}, {
name: "Josh Smith",
email: "josh#example.com",
dob: "2011-12-12"
}, {
name: "Jessie Smith",
email: "jess#example.com",
dob: "2004-10-12"
}]
}
Add Button Export
<button ng-click="exportData()"> Export </button>
Define the function in controller
$scope.exportData = function () {
var blob = new Blob([document.getElementById('exportable').innerHTML], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
});
saveAs(blob, "Report.xls");
};
Demo

Related

Excel not properly generate in angularjs

i am using angularjs and generate excel-sheet using blob with the help of filesaver.js i am getting my properly but excel will not open correctly in Micrsoft Excel it's working but i am not getting the cells it's shows black white page but content is there .help how to solve
here i attached my fiddle:https://jsfiddle.net/x30v0bym/3/
You can use the following directive,
app
.directive('excelExport',
function() {
return {
restrict: 'A',
scope: {
fileName: "#",
data: "&exportData"
},
replace: true,
template: '<button class="btn btn-primary btn-ef btn-ef-3 btn-ef-3c mb-10" ng-click="download()">Export to Excel <i class="fa fa-download"></i></button>',
link: function(scope, element) {
scope.download = function() {
function datenum(v, date1904) {
if (date1904) v += 1462;
var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
};
function getSheet(data, opts) {
var ws = {};
var range = {
s: {
c: 10000000,
r: 10000000
},
e: {
c: 0,
r: 0
}
};
for (var R = 0; R != data.length; ++R) {
for (var C = 0; C != data[R].length; ++C) {
if (range.s.r > R) range.s.r = R;
if (range.s.c > C) range.s.c = C;
if (range.e.r < R) range.e.r = R;
if (range.e.c < C) range.e.c = C;
var cell = {
v: data[R][C]
};
if (cell.v == null) continue;
var cell_ref = XLSX.utils.encode_cell({
c: C,
r: R
});
if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
} else cell.t = 's';
ws[cell_ref] = cell;
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
return ws;
};
function Workbook() {
if (!(this instanceof Workbook)) return new Workbook();
this.SheetNames = [];
this.Sheets = {};
}
var wb = new Workbook(),
ws = getSheet(scope.data());
/* add worksheet to workbook */
wb.SheetNames.push(scope.fileName);
wb.Sheets[scope.fileName] = ws;
var wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookSST: true,
type: 'binary'
});
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), scope.fileName + '.xlsx');
};
}
};
}
);
DEMO
Your fiddle contains everything needed except one important thing. You're not generating content that excel can understood.
Your problem is here:
var blob = new Blob([document.getElementById('exportable').innerHTML], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
});
More specifically, here:
[document.getElementById('exportable').innerHTML]
This returns HTML, which is not Excel file format. There is no auto-magic that converts HTML into Excel.
This is usually done on server side, not AngularJS. But if you're forced, you'll need another library to handle conversion between your data and Excel. One of popular library is ExcelJS.
Simpler solution - generate CSV
I would propose to skip Excel and generate CSV, which is the simplest possible format to generate - understood by Excel. You only have to modify your export function:
$scope.exportData = function () {
var blob = new Blob([convertToCsv($scope.items)], {
type: "text/csv"
});
saveAs(blob, "Report.csv");
function convertToCsv(items) {
var headers = "Name; Email; DoB \n";
return headers + items.map(function(item) {
return item.name + ";" + item.email + ";" + item.dob;
}).join("\n");
}
};
Function convertToCsv organize your items into format:
Name; Email; DoB
John Smith;j.smith#example.com;1985-10-10
Jane Smith;jane.smith#example.com;1988-12-22
Jan Smith;jan.smith#example.com;2010-01-02
Jake Smith;jake.smith#exmaple.com;2009-03-21
Josh Smith;josh#example.com;2011-12-12
Jessie Smith;jess#example.com;2004-10-12
Your fiddle updated: DEMO
Downloaded file Reports.csv can be opened and edited in Excel.
Notes
You won't be able to use function specific to excel as setting column width, colors etc.
My Solution with CSV is not quite what you're asked, but I believe it is still good enough
Related question: how to generate Excel through Javascript which points out other solutions
function myCtrl($scope) {
$scope.exportData = function() {
var blob = new Blob([convertToCsv($scope.items)], {
type: "text/csv"
});
saveAs(blob, "Report.csv");
function convertToCsv(items) {
var headers = "Name; Email; DoB \n";
return headers + items.map(function(item) {
return item.name + ";" + item.email + ";" + item.dob;
}).join("\n");
}
};
$scope.items = [{
name: "John Smith",
email: "j.smith#example.com",
dob: "1985-10-10"
}, {
name: "Jane Smith",
email: "jane.smith#example.com",
dob: "1988-12-22"
}, {
name: "Jan Smith",
email: "jan.smith#example.com",
dob: "2010-01-02"
}, {
name: "Jake Smith",
email: "jake.smith#exmaple.com",
dob: "2009-03-21"
}, {
name: "Josh Smith",
email: "josh#example.com",
dob: "2011-12-12"
}, {
name: "Jessie Smith",
email: "jess#example.com",
dob: "2004-10-12"
}]
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<script src="https://rawgithub.com/eligrey/FileSaver.js/master/FileSaver.js" type="text/javascript"></script>
<body ng-app>
<div ng-controller="myCtrl">
<button ng-click="exportData()">Export</button>
<br />
<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>
</div>
</body>

Get and manipulate phone contacts by ngCordova in Ionic app

I want to get all phone contacts by $cordovaContacts and manipulate them to send for server. like this
$scope.contacts = [];
$cordovaContacts.find({
filter: '',
fields: ['displayName','phoneNumbers']
}).then(function (allContacts) {
angular.forEach(allContacts, function (contact, index) {
$scope.contacts.push({
"first_name": contact.name.givenName,
"last_name": contact.name.familyName,
"phone_number": contact.phoneNumbers[0].value
});
});
});
HTML
<p style="white-space: pre;">{{contacts | json: 3}}</p>
But angular.forEach not working and there is No error, whats wrong?
Finally solve the problem ::
$cordovaContacts.find({
filter: '',
fields: ['displayName', 'name', 'phoneNumbers']
}).then(function (allContacts) {
var contacts = [];
angular.forEach(allContacts, function (contact, index) {
if (contact.phoneNumbers != null &&
contact.phoneNumbers[0].type == 'mobile' &&
contact.name != null) {
// if user have firstName and lastName
if (contact.name.givenName && contact.name.familyName) {
contacts.push({
"first_name": contact.name.givenName,
"last_name": contact.name.familyName,
"phone_number": contact.phoneNumbers[0].value
});
} else {
contacts.push({
"first_name": contact.name.givenName ? contact.name.givenName : '',
"last_name": contact.name.familyName ? contact.name.familyName : '',
"phone_number": contact.phoneNumbers[0].value
});
}
}
});
});

Pushing multiple entries and saving them in database through AngularJS

I am stuck saving multiple rows I pushed in "$scope.arr" to my SQL Server database. I have my code below and it works fine but when I click on "Save To DB" button after adding/pushing some entries by pressing "Add Person" button, it passes a row with null values to SQL Server database. Please guide me where I am doing the mistake:
I also heard about using angular.forEach loop but I am confused using that too.
I have my model class "TestModel.cs" here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TestProj.Models
{
public class TestModel
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
My MVC Controller named TestController's Add method here:
[HttpPost]
public string AddPerson(TestModel test)
using (TestContext db = new TestContext())
{
if (test != null)
{
db.TestModels.Add(test);
db.SaveChanges();
return "Successful";
}
else
{
return "Failed";
}
}
}
My AngularJS script here:
var app = angular.module("TestApp", []);
app.controller("TestCtrl", function ($scope, TestService) {
$scope.arr = [];
$scope.addPerson = function () {
var myobj = {
FirstName: $scope.firstname,
LastName: $scope.lastname
}
$scope.arr.push(myobj);
};
$scope.savePerson = function () {
var TestData = TestService.AddPer($scope.arr);
TestData.then(function (msg) {
alert(msg.data);
}, function () {
alert('Error In Adding Person');
});
};
});
app.service("TestService", function ($http) {
this.AddPer = function (person) {
var response = $http({
method: "post",
url: "/Test/AddPerson",
data: JSON.stringify(person),
dataType: "json",
});
console.log(response);
return response;
}
});
And my Index.cshtml file here:
<div ng-controller="TestCtrl">
<form>
FirstName: <input class="form-control" ng-model="firstname" /><br />
LastName: <input class="form-control" ng-model="lastname" /><br />
<button class="btn btn-success" ng-click="addPerson()">Add Person</button>
<button class="btn btn-success" ng-click="savePerson()">Save To DB</button>
<table class="table table-bordered">
<tr>
<th>S. No</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
<tr ng-repeat="a in arr">
<td>{{$index+1}}</td>
<td>{{a.FirstName}}</td>
<td>{{a.LastName}}</td>
</tr>
</table>
</form>
</div>
<script src="~/Scripts/MyAngular/Module.js"></script>
Your help will be appreciated. Thanks!
Then server side action should expect collection of TestModel, you could us List there. If you [FromBody] before parameter, you don't need to stringify data before posting it to server.
Code
[HttpPost]
public string AddPerson([FromBody] List<TestModel> test)
using(TestContext db = new TestContext()) {
test.forEach(t=> {
if (t != null) {
db.TestModels.Add(t);
return "Successful";
} else {
return "Failed";
}
});
db.SaveChanges(); //save context at the end, when no error occurs
}
}
The problem was in my Server Side Code i.e, my C# controller, this method worked:
[HttpPost]
public void AddPerson(List<TestModel> test)
{
using (TestContext db = new TestContext())
{
foreach(var t in test)
{
if (t != null)
{
db.TestModels.Add(t);
Console.WriteLine("Success");
}
}
db.SaveChanges(); //save context at the end, when no error occurs
}
}

How to get and compare values in table from another table in angularjs?

I am new at angularjs. So, it might be fool question.Anyway, please let me explain my problem. I have a table which is listed by ng-repeat and I'd like to change a column datas with another datas in another table column.
<tr data-ng-repeat=" list in listTypes">
<td>{{list.Comments}}</td>
<td>{{list.Modul}}</td>
<td>{{list.UserId}}</td>
<td data-ng-repeat="user in userNames">{{user.UserName}}</td>
I want to get UserName instead of UserId, but the problem that UserName is recorded in another table. Here is my angular for getting listTypes :
$scope.GetList = function () {
var onSuccess = function (response, status) {
//1
$scope.listTypes = response.Data;
var str = response.Data;
$scope.listTypes = eval('(' + str + ')');
for (var key in $scope.listTypes) {
$scope.listTypes[key].selected = "";
}
$scope.GetUserNames();
};
var data = null;
var request = $rest.GetList(data);
NGTools.CallNgServiceWithRequest(request, onSuccess, "GetList");
};
And trying to get usernames with this code:
$scope.userdatas= [];
$scope.userNames = [];
$scope.GetUserNames = function () {
var onSuccess = function (response, status) {
//1
$scope.userNames = response.Data;
};
$scope.userdatas= $scope.listTypes.UserId;
var data = { userdatas: JSON.stringify( $scope.userdatas) };
var request = $rest.GetUserNames(data);
NGTools.CallNgServiceWithRequest(request, onSuccess, "GetUserNames");
};
but it doesn't work. I couldn't figure out what's wrong with this code block. Please let me know if any tip is available. Thank you!
Assuming that you have to collections in your scope - one of which holds the id of the user, and the other holding the name, like so:
$scope.users = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
{ id: 3, name: 'Janice Doe' } ];
$scope.userInfo = [
{ userId: 1, gender: 'male' },
{ userId: 2, gender: 'female' },
{ userId: 3, gender: 'female' }];
Then what you could do is ng-repeat over the one with the userInfo and in your binding expression - use the id to get the name from the other collection:
<li ng-repeat="item in userInfo">
{{ item.gender }} {{ getNameFor(item.userId) }}</li>
Where the getNameFor is defined as:
$scope.getNameFor = function(id) {
var user = $scope.users.filter(function(item) { return item.id === id })[0];
console.log(user);
return user.name;
Which I checked in a fiddle here: http://jsfiddle.net/01kmoxw9/

angularJs filter nested object Track by

I created a custom filter, but its giving me an error
I created a fiddle here:
Fiddle
I have this user data:
data: [{
profile: {
firstName: 'John',
lastName: 'OConner'
}
}, {
profile: {
firstName: 'Smith',
lastName: 'OConner'
}
}, {
profile: {
firstName: 'James',
lastName: 'Bond'
}
}]
And I need to filter by the nested obj - profile by this
data: [{
column: {
label: 'firstName',
}
}, {
column: {
label: 'lastName',
}
}]
I can filter but is giving me this error:
this is my filter:
myApp.filter('testFilter', ['$filter',
function($filter) {
return function(items, selectedFilter) {
var returnArray = items;
var filtered = [];
var process = {};
process.filtered = [];
process.loop = function(obj, key) {
var filtered = [];
this.obj = obj;
this.key = key;
// console.log('obj--> ', obj);
// console.log('key--> ', key);
filtered = filtered.concat($filter('filter')(items, process.superFilter));
if (filtered.length > 0) {
process.filtered = filtered;
}
};
process.superFilter = function(value) {
var returnMe;
var originalValue = value.profile[process.key];
if (typeof(value) === 'String') {
originalValue = originalValue.toLowerCase();
}
if (originalValue === process.obj) {
console.log('found');
returnMe = value;
return returnMe;
}
};
if (Object.getOwnPropertyNames(selectedFilter).length !== 0) {
angular.forEach(selectedFilter, function(obj) {
filtered = filtered.concat($filter('filter')(items, obj));
});
returnArray = filtered;
// console.log('selectedFilter ', selectedFilter);
}
return returnArray;
};
}
]);
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. How can I solve this issue?
You need to use track by as the error suggests. If you don't have a unique key to use you can use $index.
ng-repeat='talent in talents.data | testFilter:filterInput track by $index'
Here is a working example with your code: http://jsfiddle.net/hwT4P/

Resources