AngularJS keep adding item in a scope - angularjs

I made a dummy data for my application using this method in my controller:
$scope.owners = [];
for (var i = 1; i <= 14; i++) {
$scope.owners.push({
id: i,
systemCode: '00' + (1284538+i),
systemName: 'Desktop',
description: 'Lorem ipsum dolor',
operational: '2973238',
business: '5621253',
manager: 'Dr. Smith',
status: 'Active'
});
}
and adding this function to perform delete request:
$scope.rowDelete = function(row){
$scope.owners.splice((this.owner.id - 1), 1);
}
and a page filter to use for pagination:
MyApp.filter('page', function () {
return function (list, page, size) {
var start = 0, end = 0;
//console.log('page filter...', page, size);
page = page || 1;
if (angular.isArray(list)) {
end = list.length;
if (page && Number(page) && size && Number(size)) {
page = Number(page);
size = Number(size);
start = (page - 1) * size;
end = start + size;
}
return Array.prototype.slice.call(list, start, end);
}
return null;
}
});
In the HTML template, I'm using ng-repeat and the custom filter:
<tr data-ng-repeat="owner in owners | page:$stateParams.page:2">
<td class="table-cell-clean">
<i class="mg-icon-delete"></i>
</td>
<td>{{ owner.systemCode }}</td>
<td>{{ owner.systemName }}</td>
<td>{{ owner.description }}</td>
<td>{{ owner.operational }}</td>
<td>{{ owner.business }}</td>
<td>{{ owner.manager }}</td>
<td class="align-center">{{ owner.status }}</td>
Whenever I click the delete button, rowDelete() is called as expected, the row is deleted, but as soon as it's leaving the DOM, a new item from the next page is inserted into the current table.
What might go wrong?

That would be as expected. Your owners array is being modified which requires the ng-repeat expression to be re-evaluated which includes the page filter expression as apart of it. So owners is modified by deleting a row, ng-repeat detects the change and re-evaluates the repeat expression which before it can iterate over the array it runs owners through the page filter. So n items per page, delete leaves n-1, and page filters the owners array to return n items.
All normal behavior.

Related

Call service for each element found from another service and then populate an array with results

Problem:
Trying to populate a html table with two services. First I'm calling a service to retrieve all products. Then for each element I'm calling another service that accepts the fabricplanid as a parameter and returns the object if exists. If exists I push it to an array, but if returns an error 404 I push a string to the same array.
The problem I'm facing is that the values inside the array, don't match the corresponding fabricPlanId of the product.
This is the product.component.ts file that when called execute this service and populates a table using a ngfor.
products:Product[];
//view
fabricplan: Fabricplan[];
plan_desc: Array<String> = [];
//view select
fabricplans: Fabricplan[];
ngOnInit() {
this.productservice.getProducts().subscribe(products => {
this.products = products;
console.log("Produtos", this.products);
this.products.forEach( element => {
console.log(element.fabricPlanId);
this.fabricplanservice.getfabricplan(element.fabricPlanId).subscribe(
(response) => {
this.fabricplan = response;
this.plan_desc.push(this.fabricplan['description']);
},
(error) => {
if(error.status === 404){
this.plan_desc.push('No fabric plan');
}
});
});
});
console.log("Planos de fabrico", this.plan_desc);
}
The product.component.html file
<table class="table table-hover">
<thead class="thead-dark">
<tr>
<th scope="col">Number</th>
<th scope="col">Fabric Plan</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Price €</th>
<th scope="col">Ative</th>
</tr>
</thead>
<tbody>
<tr scope="row" id="table" *ngFor="let product of products let i = index"(click)="open(content, product.id)">
<td>{{ i+1 }}</td>
<td>{{ plan_desc[i] }}</td>
<td>{{ product?.name }}</td>
<td>{{ product?.description }}</td>
<td>{{ product?.price }}</td>
<td>{{ product?.active }}</td>
</tr>
</tbody>
</table>
Response body
Products
{
active: true,
description: " descrição do prod 1",
fabricPlanId: 1,
id: 1,
name: "Produto 1",
price: 1
}
FabricPlans
{
dateStart: "2019-10-30T00:00:00"
description: "Descrição do plano 1"
id: 1
operationsIds: [1, 2]
}
Based on your comment, the plan_desc are in a random order. The cause for this is
this.products.forEach( element => {
this.fabricplanservice.getfabricplan(element.fabricPlanId).subscribe(
...
you cannot control how long each request will take, so some will return sooner, some later and whenever they return, they are added to the list --> the order is random.
However it is pretty easy to make a request for each item and then get the list order with rxjs forkjoin
// create a request for each product (dont fire it yet)
const plans_desc$ = this.products.map( element =>
this.fabricplanservice.getfabricplan(element.fabricPlanId).pipe(
// map it to the value you want
map((response) => {
this.fabricplan = response;
return this.fabricplan['description'];
}),
// replace 404 responses with the 'No fabric plan'
// if not 404 throw the error again
catchError((error) => {
if(error.status === 404){
return 'No fabric plan';
} else {
throwError(error);
}
}));
});
// now make the actuall requests. Forkjoin will return, when all requests are completed.
// the order will be the be how the requests where added, not when the completed
forkJoin(plans_desc$).subscribe((plans_desc) => this.plan_desc = plans_desc);
(I wrote his code here)
imports:
import {forkJoin, throwError} from 'rxjs';
import {map, catchError} from 'rxjs/operators';

Prevent users from selecting same value in multiple dropdowns

I am loading a table from an API call , table rows are dynamic and it is based on the returned values from API call. I am displaying sort order and value should be unique and user shouldn't select a previously selected values. I tried to follow as per this (http://jsfiddle.net/jnash21/oqezom4y/) but i am not able to achieve as mine is dynamic.
I tried this (http://embed.plnkr.co/QU4r05n9rQprwyL9Ltxh/) .
editor.controller('EditorController', function($scope) {
$scope.entities = [{name:"pencil",sortOrder:""} ,{name:"notepad",sortOrder:""} ,
{name:"bookshelf",sortOrder:""}
];
$scope.sortOrderValues=[1,2,3];
});
<table>
<tr ng-repeat="x in entities">
<td>{{ x.name }}</td>
<td><select ng-model="x.sortOrder"
ng-options="col for col in sortOrderValues">
</select>
<span ng-show="!x.sortOrder"> * sort order required</span>
</td>
</tr>
</table>
How can I prevent a user from selecting same sort order in each row using angular js?
This plunker might help you.
First of all, genereate an array from 1 to entities.length (this case, 3).
When you select an option, tirgger the optionSelected function. This function will generate your inital array and calculate the used sortOrders by your entities. Then it filters the second ones from the first array.
HTML
<div ng-controller="EditorController">
<table>
<tr ng-repeat="x in entities">
<td>{{ x.name }}</td>
<td><select ng-model="x.sortOrder"
ng-options="col for col in sortOrderValues"
ng-change="optionSelected()">
</select>
<span ng-show="!x.sortOrder"> * sort order required</span>
</td>
</tr>
</table>
</div>
CONTROLLER
editor.controller('EditorController', function($scope) {
$scope.entities = [{name:"pencil",sortOrder:""} ,{name:"notepad",sortOrder:""} ,
{name:"bookshelf",sortOrder:""}
];
// Genereate all the numbers between 1 and $scope.entities.length
$scope.sortOrderValues= $scope.entities.map(
function (item, index) {
return index + 1;
}
);
// Function executed when you select a sortOrder
$scope.optionSelected = function () {
// Genereate all the numbers between 1 and $scope.entities.length
var allIndexes = $scope.entities
.map(function (entity, index) { return index + 1; });
// Get all the sortOrder used
var usedIndexes = $scope.entities
.map(function(e) { return e.sortOrder; });
// Remove from the [1, .., $scope.entities.length] array all the sortOrder used
$scope.sortOrderValues = allIndexes
.filter(function (order) {
return !usedIndexes.find(function(index) { return index === order; });
});
}
});

Updating paginated table after filter applied AngularJS

I have this example:
https://jsfiddle.net/3noebzgr/1/
Everything works except when I filter by jersey number I cannot update the pagination. So if I filter for jersey numbers 1-3, jersey 1 is on page 1, jersey 2 is on page 2 and jersey 3 is on page 4, leaving page 3 empty. I would like for the table to re-paginate to have the filtered list on one page (or more depending on number of items).
This is my paginate function:
$scope.paginate = function(value) {
var begin, end, index;
begin = ($scope.currentPage - 1) * $scope.numPerPage;
end = begin + $scope.numPerPage;
index = $scope.players.indexOf(value);
return (begin <= index && index < end);
};
This is where it is applied:
<tr ng-repeat="player in players | orderBy:sortType:sortReverse | filter:byRange('Number', minNum.Number, maxNum.Number) | filter:paginate">
<td>{{ player.Name }}</td>
<td>{{ player.Number }}</td>
<td>{{ player.Position }}</td>
<td>{{ player.Team }}</td>
</tr>
The filters:
<select name="minNum" ng-model="minNum" ng-options="player.Number for player in players | orderBy:'Number'">
<option value="">All</option>
</select>
and
<select name="maxNum" ng-model="maxNum" ng-options="player.Number for player in players | orderBy:'Number'">
<option value="">All</option>
</select>
I have tried other stack overflow suggestions but none have worked so far.
Rerender $scope.numPerPage to be equal $scope.players.length when maxnumber is clicked so that the num per page will be equal players length. Thanks
$scope.paginate = function(value) {
var begin, end, index;
begin = ($scope.currentPage - 1) * $scope.numPerPage;
end = begin + $scope.numPerPage;
index = $scope.players.indexOf(value);
if ($scope.maxNum !== undefined) {
$scope.numPerPage = $scope.players.length;
}
return (begin <= index && index < end);
};

How to use ng-click value as ng-repeat property?

How to get $scope.clickedVal in r.clickedVal?
Button click calls a function, passing in two parameters:
<a ng-click="display('Info', 'Admission_Info'); filters.Admission_Info= '!!'; ">Info</a>
<a ng-click="display('Info', 'SomeOther_Info'); filters.SomeOther_Info= '!!'; ">Other</a>
Function in the controller:
$scope.display = function (col, val) {
$scope.filters = {};
$scope.clickedColumn = col;
$scope.clickedVal = val;
};
Table displays a filtered view with 2 columns.
<tr ng-repeat="r in response | filter:filters as filtered " ng-show="filtered.length">
<td>{{ r.Name }}</td>
<td>{{ r.clickedVal }}</td>
</tr>
{{ r.{{clickedVal}} }} didn't work.
So for example, if the first button is clicked, r.clickedVal should return r.SomeOther_Info.
I've tried creating a filter as well but run into the same issue - m.value is incorrect.
SchoolProgramsApp.filter('myFilter', function () {
return function (input, column, value) {
var out = [];
angular.forEach(input, function (m) {
if (m.value === value) {
out.push(m)
}
})
return out;
}
});
You should use bracket notation to access property of the object by variable name:
{{ r[clickedVal] }}

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