Inconsistent Data on multiple page printing on a printer - angularjs

Requirement
To print three(depends on the server response size) pages of print on one button click event.
Stored a barcode image an array, and loop through that array and bind the value to ctrl.barCodeImage. Then call the print service to print each bar code in different page. But it print always three same value that is the last value in the array.
It is a three separate pages with different bar code data in it.
This is the expected response
print 1
print 2
print 3
Current response is inconsistent.
It will come all pages same value , which is the last value in that array.
Implementation Details:
Created an DOM, which will be printed each time with different value assigned to it.
<div id="printThisElement" class="onlyprint" >
<table>
<tr>
<td>{{ ctrl.instCode }}</td>
<td align="center">{{ ctrl.date | dateDisplayFilter}} </td>
</tr>
<tr>
<td colspan="2" align="center"> <img ng-src="data:image/JPEG;base64,{{ctrl.barCodeImage}}"> </td>
</tr>
<tr>
<td colspan="2" align="center">{{ ctrl.user.name }} </td>
</tr>
<tr>
<td >Reg Id: {{ ctrl.regIdLookup }}</td>
<td align="center">{{ ctrl.testName }}</td>
</tr>
</table>
</div>
The print function which is getting called on the button click, added timeout to get assigned all the values on the print div.
vm.print = function() {
var res = [];
var sampleId = [];
var noTest = false;
angular.forEach(vm.gridOptions.data, function(item) {
if (item.sample != null) {
sampleId.push(angular.copy(item.sample.sampleId));
}
})
if(sampleId != null){
UserService.getInstitute(vm.user.instCode).then(function(response) {
vm.instCode = response.data.result.estName;
});
var userServicePromise = UserService.printBarCodes(sampleId);
userServicePromise.then(function(response) {
if (response != null && response.data != null && response.data.result != null) {
response.data.result.forEach(function(entry) {
vm.barCodeImage = angular.copy(entry);
$timeout(function() {
PrintService.printElement("printThisElement");
}, 0);
});
} else {
toaster.error(response.data.message);
}
});
}
}
}
Print Service, which is used to print the DOM.
(function() {
'use strict';
angular.module('app.services')
.factory('PrintService', PrintService);
PrintService.$inject = [];
function PrintService() {
var service = {
printElement: printElement
};
return service;
function printElement(elem) {
var printSection = document.getElementById('printSection');
// if there is no printing section, create one
if (!printSection) {
printSection = document.createElement('div');
printSection.id = 'printSection';
document.body.appendChild(printSection);
}
var elemToPrint = document.getElementById(elem);
// clones the element you want to print
var domClone = elemToPrint.cloneNode(true);
printSection.innerHTML = '';
printSection.appendChild(domClone);
window.print();
window.onafterprint = function() {
printSection.innerHTML = '';
}
};
}
})();
Not able to figure out why it gives inconsistent print data on each time. I guess it might be synchronous issue.
But most of the time it displays the last data in all three page of print.Thanks in advance.
Plunk here https://plnkr.co/edit/jwoC0bNQJ9J92l5S8ZJJ?p=preview
Any HELP ?

I forked your plunker and I use a queue to allow multiple printing
https://plnkr.co/edit/xZpcx6rCAUo9SemUPTt5?p=preview
I have a print function
function print(data) {
var domClone = '<div id="printThisElement" class="onlyprint" >'+
'<table>'+
'<tr> '+
'<td>{{ data.instCode }}</td>'+
'<td align="center">{{ data.date}} </td>'+
'</tr>'+
'<tr> '+
'<td colspan="2" align="center"> <img ng-src="data:image/JPEG;base64,{{data.barCodeImage}}"> </td>'+
'</tr>'+
'<tr> '+
'<td colspan="2" align="center">{{ ctrl.user.name }} </td>'+
'</tr>'+
'<tr> '+
'<td >Reg Id: {{ data.regIdLookup }}</td>'+
'<td align="center">{{ data.testName }}</td>'+
'</tr>'+
'</table>'+
'</div>'
printSection.innerHTML = '';
var scope = $rootScope.$new();
scope.data = data;
var domTemp = $compile(domClone)(scope)[0];
printSection.appendChild(domTemp);
$timeout(function(){
onPrintFinished(window.print());
}, 0);
}
And in PrintElement function i put in queu if printing is in progress :
if(!printInProgress) {
printInProgress = true;
print(data)
}
else {
queue.push(data);
}
At the end of printing run new printing with new data:
function onPrintFinished (printed){
var next = queue.shift();
if(next) {
console.log(next, queue);
$timeout(function() {
print(next);
});
}
else {
printInProgress = false;
}
}
I hope this time you have that you want

The problem is that vm.barCodeImage is set before corresponding PrintService.printElement is actually executed. So the sequence is:
vm.barCodeImage = angular.copy(first entry);
vm.barCodeImage = angular.copy(second entry);
vm.barCodeImage = angular.copy(third entry);
PrintService.printElement("printThisElement");
PrintService.printElement("printThisElement");
PrintService.printElement("printThisElement");
The solution is to modify your code in the following way:
$timeout(function() {
vm.barCodeImage = angular.copy(entry);
PrintService.printElement("printThisElement");
}, 0);
Thanks to that each call PrintService.printElement will use proper data and not the last element in the array.

Related

Angular.forEach converting values to use in sort table

I'm trying to sort through my list in Angular, but it's not working.
The default sort order should be by Fiscal Year descending, but some 2019 values are being sorted after 2020 values:
https://i.imgur.com/1F9JM2V.png
Then numbers are being sorted incorrectly when you click on that column sort:
https://i.imgur.com/67fMJ8V.png
And the end date has no sorting structure. I am setting it to MM/dd/yyyy format in the view, not sure if that has any bearing:
https://i.imgur.com/dOcnFBt.png
Only tried reverse and descending orders. Not sure if there are any syntax or built-in ways of sorting.
Controller:
function init() {
$scope.loading = true;
$scope.rfrorder = {
Orderby: 'rfrFY',
descending: false
};
ContractsService.getRefRFRInformation()
.then(function (results) {
$scope.refRFRInfo = results.data;
angular.forEach($scope.refRFRInfo, function (value) {
value.edit = true;
value.editMode = false;
if (value.endDate == null) {
value.edit = false;
}
});
$scope.loading = false;
});
}
$scope.rfrSorting = function (column) {
var sort = $scope.rfrorder;
if (sort.Orderby == column) {
sort.descending = !$scope.rfrorder.descending;
} else {
sort.Orderby = column;
sort.descending = false;
}
};
$scope.rfrselected = function (column) {
if (column == $scope.rfrorder.Orderby) {
return ('tablesort-icon glyphicon glyphicon-arrow-' + (($scope.rfrorder.descending) ? 'down' : 'up'));
}
else {
return 'tablesort-icon glyphicon glyphicon-sort';
}
};
View:
<thead class="headercolor">
<tr class="thead">
<th ng-click="rfrSorting('rfrFY')"><div class="tablesort-header">RFR FY <i ng-class="rfrselected('rfrFY')"></i></div></th>
<th ng-click="rfrSorting('rfrNumber')"><div class="tablesort-header">RFR Number <i ng-class="rfrselected('rfrNumber')"></i></div></th>
<th ng-click="rfrSorting('rfrEffectiveDate')"><div class="tablesort-header">Effective Date <i ng-class="rfrselected('rfrEffectiveDate')"></i></div></th>
<th ng-click="rfrSorting('rfrEndDate')"><div class="tablesort-header">End Date <i ng-class="rfrselected('rfrEndDate')"></i></div></th>
<th ng-click="rfrSorting('rfrModifiedDate')"><div class="tablesort-header">Modified Date <i ng-class="rfrselected('rfrModifiedDate')"></i></div></th>
<th ng-click="rfrSorting('rfrModifiedBy')"><div class="tablesort-header">Modified By <i ng-class="rfrselected('rfrModifiedBy')"></i></div></th>
<th></th>
</tr>
</thead>
<tbody class="form-group form-group-sm">
<tr ng-repeat-start="rfrDetail in refRFRInfo | orderBy:rfrorder.Orderby:rfrorder.descending">
EDIT
I believe it has to do with the numbers coming back as string. I'm trying to find a way that will go through and convert the numbers, then put them back in an object I can display on my view.
ContractsService.getRefRFRInformation()
.then(function (results) {
$scope.refRFRInfo = results.data;
angular.forEach($scope.refRFRInfo, function (value) {
//$scope.model.refRFRInfo.rfrNumber = parseInt(value.rfrNumber);
//$scope.model.refRFRInfo.rfrFY = parseInt(value.rfrFY);
//$scope.model.refRFRInfo.endDate = Date.parse(value.endDate);
$scope.number.push(value.rfrNumber);
value.edit = true;
value.editMode = false;
//new Date(value.startDate).withoutTime() <= new Date().withoutTime() &&
if (value.endDate == null) {
// value.editMode = true;
value.edit = false;
}
});
$scope.loading = false;
});
I understand the basic principle as I have $scope.number for a validation, but I don't know how to iterate through the entire object and then create a new object with the proper values.
The problem is with your sorting code.
Try looking at this post:
AngularJS sorting rows by table header

Push and splice into array when checkall and checkbox is checked in angularjs

I am trying to push and splice the elements based on checkall, single checkbox clicked, my problem is I am getting a list from angularjs post request and displayed it using ng-repeat I have given provision to enter some text in a new column along with ng-repeat data. Now based on the user selection of checkall or single checkbox clicked I am pushing the data into array. Here I am able to push the data when the user clicked on single checkbox, but when the user clicked on chekall checkbox 0, 1 are pushing the array instead of textbox value. Any help will be greatly appreciated.
Html
<table class='reportstd' align='center' width='80%'>
<tr class='trdesign'>
<td>
<input type="checkbox" name="checkAll" id="all" data-ng-model="checkedAll" data-ng-change="toggleCheckAll()" />
</td>
<td> Sl No</td>
<td> RO No.</td>
<td> Truck No.</td>
</tr>
<tr data-ng-repeat="user in RosList">
<td> <input type="checkbox" value="{{user.do_ro_no}}" data-ng-model="user.checked" data-ng-change="modifyArrayToPost(user,truck_no[$index])" /> </td>
<td>{{$index + 1}}</td>
<td>{{user.do_ro_no}}</td>
<td><input type='text' data-ng-model="truck_no[$index]" id="truck_no_{{$index}}" name="truck_no_{{$index}}" value=""></td>
</tr>
</table>
<table>
<tr>
<td colspan='2'><input type="submit" id="btn_submit" name='sea' value='Search' data-ng-submit="postROs(arrayToPost)" /></td>
</tr>
</table>
Angularjs
$scope.arrayToPost = [];
$scope.toggleCheckAll = function() {
if ($scope.checkedAll) {
angular.forEach($scope.RosList, function(user, truckno) {
user.checked = true;
$scope.modifyArrayToPost(user, truckno);
});
} else {
angular.forEach($scope.RosList, function(user, truckno) {
user.checked = false;
$scope.modifyArrayToPost(user, truckno);
});
}
}
$scope.modifyArrayToPost = function(user, truckno) {
if (user.checked && truckno != null && $scope.arrayToPost.indexOf(user.do_ro_no) == -1) {
$scope.arrayToPost.push(user.do_ro_no, truckno);
} else if (!user.checked) {
$scope.arrayToPost.splice($scope.arrayToPost.indexOf(user.do_ro_no, truckno), 2);
}
}
$scope.$watch('RosList', function() {
var allSet = true;
var allClear = true;
angular.forEach($scope.RosList, function(user, truckno) {
if (user.checked) {
allClear = false;
} else {
allSet = false;
}
});
var checkAll = $element.find('#all');
checkAll.prop('indeterminate', false);
if (allSet) {
$scope.checkedAll = true;
} else if (allClear) {
$scope.checkedAll = false;
} else {
$scope.checkedAll = false;
checkAll.prop('indeterminate', true);
}
}, true);
$scope.RosList = [
{do_ro_no: "217PALV000201898", slno: 1, },
{do_ro_no: "317PALV000201898", slno: 2, }
]
truck_no model is not coming from RosList.
You should initialize truck_no in your controller as $scope.truck_no = [] in order to access the values, and in your $scope.toggleCheckAll function change $scope.modifyArrayToPost(user, truckno); to $scope.modifyArrayToPost(user, $scope.truck_no[truckno]);
EDIT:
I've slightly modified your code to handle all cases.
Demo: https://next.plnkr.co/edit/DnzsCFkPQU8ByFZ8
If I understand the issue correctly, I think that the solution is much simpler. The main confusation is that there is not just only one "source of truth" - you hold a state for each row and also all the do_ro_no's.
I suggest to keep track only for each row and calculate the arrayToPost whenever you need.
Like this:
angular.module('app', []).controller('ctrl', ($scope, $element) => {
$scope.truck_no = [];
$scope.RosList = [{
do_ro_no: "217PALV000201898",
slno: 1,
},
{
do_ro_no: "317PALV000201898",
slno: 2,
}
];
$scope.getTruckNo = () => {
return $scope.truck_no.filter((t, index) => {
return $scope.RosList[index].checked;
});
}
$scope.getArrayToPost = () => {
return $scope.RosList
.filter(ros => ros.checked)
.map(ros => ros.do_ro_no);
}
$scope.arrayToPost = [];
$scope.toggleCheckAll = function() {
if ($scope.checkedAll) {
//angular.forEach($scope.RosList, function(user, truckno) {
// user.checked = true;
// $scope.modifyArrayToPost(user, truckno);
//});
$scope.RosList.forEach(ros => ros.checked = true);
} else {
//angular.forEach($scope.RosList, function(user, truckno) {
// user.checked = false;
// $scope.modifyArrayToPost(user, truckno);
//});
$scope.RosList.forEach(ros => ros.checked = false);
}
}
//$scope.modifyArrayToPost = function(user, truckno) {
// if (user.checked && truckno != null && $scope.arrayToPost.indexOf(user.do_ro_no) == -1) {
// $scope.arrayToPost.push(user.do_ro_no, truckno);
// } else if (!user.checked) {
// $scope.arrayToPost.splice($scope.arrayToPost.indexOf(user.do_ro_no, truckno), 2);
// }
//}
//$scope.$watch('RosList', function() {
// var allSet = true;
// var allClear = true;
// angular.forEach($scope.RosList, function(user, truckno) {
// if (user.checked) {
// allClear = false;
// } else {
// allSet = false;
// }
// });
//
// var checkAll = $element.find('#all');
// checkAll.prop('indeterminate', false);
// if (allSet) {
// $scope.checkedAll = true;
// } else if (allClear) {
// $scope.checkedAll = false;
// } else {
// $scope.checkedAll = false;
// checkAll.prop('indeterminate', true);
// }
//}, true);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<table class='reportstd' align='center' width='80%'>
<tr class='trdesign'>
<td>
<input type="checkbox" name="checkAll" id="all" data-ng-model="checkedAll" data-ng-change="toggleCheckAll()" />
</td>
<td> Sl No</td>
<td> RO No.</td>
<td> Truck No.</td>
</tr>
<tr data-ng-repeat="user in RosList">
<td> <input type="checkbox" value="{{user.do_ro_no}}" data-ng-model="user.checked" data-ng-change="modifyArrayToPost(user,truck_no[$index])" /> </td>
<td>{{$index + 1}}</td>
<td>{{user.do_ro_no}}</td>
<td><input type='text' data-ng-model="truck_no[$index]" id="truck_no_{{$index}}" name="truck_no_{{$index}}" value=""></td>
</tr>
</table>
<table>
<tr>
<td colspan='2'><input type="submit" id="btn_submit" name='sea' value='Search' data-ng-submit="postROs(arrayToPost)" /></td>
</tr>
</table>
<pre>
{{getTruckNo() | json}}
</pre>
</div>
The array is the result of getTruckNo() as you can see in the snippet.

Generated password using angular js not working

I want to make the password is auto generated and to pass it to the controller :
this is my view.jsp:
<tr>
<td class="leftDetails">Password</td>
<td class="rightDetails"><input type="text" name="password" ng-model="newUser.password" ng-disabled="isEditMode" id="p"/></td>
<td class="rightDetails"><input type='button' value ='generate' onclick='document.getElementById("p").value = Password.generate(16)' ng-show="add"></td>
<td class="leftDetails"></td>
<td class="rightDetails"></td>
</tr>
<script>
var Password = {
_pattern : /[a-zA-Z0-9_\-\+\.]/,
_getRandomByte : function()
{
if(window.crypto && window.crypto.getRandomValues)
{
var result = new Uint8Array(1);
window.crypto.getRandomValues(result);
return result[0];
}
else if(window.msCrypto && window.msCrypto.getRandomValues)
{
var result = new Uint8Array(1);
window.msCrypto.getRandomValues(result);
return result[0];
}
else
{
return Math.floor(Math.random() * 256);
}
},
generate : function(length)
{
return Array.apply(null, {'length': length})
.map(function()
{
var result;
while(true)
{
result = String.fromCharCode(this._getRandomByte());
if(this._pattern.test(result))
{
return result;
}
}
}, this)
.join('');
}
};
My problem is want to pass the generated password to my controller ,it was empty
How to correct that. thanks in advance?
The angular way to solve this issue is to update your input ng-model instead of selecting the element (Jquery way).
Instead of onClick use ng-click="newUser.password = Password.generate(16)"

json update only one value if its a new value in the json

This was my previous solution to get info from a json but I want to do it so that I print out a table and show the info, and only update the changes in the code, but not all in the table. Right now, when one person update it changes for all.
app.controller('list', function($scope, $http, $interval) {
$interval(function () {
$http.get('http://localhost/mvcang/list_it/sell/SWISH')
.then(function(data) {
if($scope.phones != data.data){
data = data.data;
$scope.phones = data;
}
});
}, 500);
});
So, it should look like this:
<table>
<tr>
<td>value</td>
<td>value2</td>
<td>value3</td>
</tr>
<tr>
<td>value</td>
<td>value2</td>
<td>value3</td>
</tr>
</table>
If a value in number one value is changed just update that, but not the whole thing, and if its something new, then add it to the list in the order it prints out.
i = 0;
var arr = this.old.split(', ');
angular.forEach($scope.phones, function(value, key){
if(arr[i] != value.data.profile.username ){
this.customHtml += '<li>' + value.data.profile.username + '</li>';
this.old += ', '+value.data.profile.username;
}
i++;
});
Here's something I thought of but can't integrate, or rethink the whole.

Mutliplr print functionality return inconsistent data , on changing $timeout value

I want to print a multiple barcode slip, each will have different barcode.
Using a print service to print the div content,
(function() {
'use strict';
angular.module('app.services')
.factory('PrintService', PrintService);
PrintService.$inject = [];
function PrintService() {
var service = {
printElement: printElement
};
return service;
function printElement(elem) {
var printSection = document.getElementById('printSection');
// if there is no printing section, create one
if (!printSection) {
printSection = document.createElement('div');
printSection.id = 'printSection';
document.body.appendChild(printSection);
}
var elemToPrint = document.getElementById(elem);
// clones the element you want to print
var domClone = elemToPrint.cloneNode(true);
printSection.innerHTML = '';
printSection.appendChild(domClone);
window.print();
window.onafterprint = function() {
printSection.innerHTML = '';
}
};
}
})();
Using this print service, will print the slip. Slip data will bind.
var userServicePromise = UserService.printBarCodes(sampleId);
userServicePromise.then(function(response) {
if (response != null && response.data != null && response.data.result != null) {
response.data.result.forEach(function(entry) {
/* $timeout(function() {
vm.barCodeImage = angular.copy(entry);
}, 0);*/
//vm.testName = item.testMast.testName.slice(0, 3);
vm.barCodeImage = angular.copy(entry);
$timeout(function() {
PrintService.printElement("printThisElement");
}, 1);
});
} else {
toaster.error(response.data.message);
}
});
This is the html which will be printed eventually, using DOM element id for printing.
<div id="printThisElement" class="onlyprint" >
<table>
<tr>
<td>{{ ctrl.instCode }}</td>
<td align="center">{{ ctrl.date | dateDisplayFilter}} </td>
</tr>
<tr>
<td colspan="2" align="center"> <img ng-src="data:image/JPEG;base64,{{ctrl.barCodeImage}}"> </td>
</tr>
<tr>
<td colspan="2" align="center">{{ ctrl.user.name }} </td>
</tr>
<tr>
<td >Reg Id: {{ ctrl.regIdLookup }}</td>
<td align="center">{{ ctrl.testName }}</td>
</tr>
</table>
</div>
Expected out put is three slips with different barcode:
7865
7866
7867
Output is three slips with same barcode
7865
7865
7865
some times,
7866
7866
7866
On changing the $timeout(function() value output be like
7865
7865
7866
what can be the reason for this ?
Never ever modify the DOM from inside a service, that's just totally against the whole way Angular works. What you should do instead is create a model of the data (and it's quite alright to create that in the service) and use Angular's templates to render that model in the page.
The reason your code doesn't work is that you are trying to re-use vm.barCodeImage for different images on the page. Angular tracks the changes and will redraw existing parts of the page. That is why you get the same barcode repeated: the different copies each use the same model so they will be the same.
A simple solution is to create an array ofvm.barCodeImages and then just render them in an ng-repeat loop. A better way might be to create a myBarcode directive which uses UserService.printBarCodes to create one barcode in an isolated scope and then your template will look shorter and tidier.

Resources