Prevent users from selecting same value in multiple dropdowns - angularjs

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; });
});
}
});

Related

AngularJS - two ng-repeats where first.id =second.id

I am trying to show name of city against the city id I receive from branches. What is the angular way of doing it?
$http.get(baseURL + '/city/getList?token=' + token)
.then(function(response) {
$scope.cities = response.data.data;
});
$http.get(baseURL + '/store/getAllBranch?token=' + token)
.then(function(response) {
$scope.branches= response.data.data;
});
<tbody>
<tr ng-repeat="x in branches| orderBy:'name'">
<td>{{x.name}}</td>
<td>{{x.address}}</td>
<td ng-repeat="city in cities track by x.city">{{city.name}}</td>
</tr>
</tbody>
Create a filter to convert cityID to cityName
<tbody>
<tr ng-repeat="x in branches| orderBy:'name'">
<td>{{x.name}}</td>
<td>{{x.address}}</td>
<td>{{x.cityId | cityName}}</td>
</tr>
</tbody>
app.filter('cityName', function(cityId) {
return function(cityId){
for(var ci = 0; ci < cities.length; ci++)
{
if( cities[ci].id == cityId )
return cities[ci].name;
}
return '';
};
});
And use it like that..
But this way is not good for performance. If you could declare related city names in related branches then you can just print that to screen with no calculation.
The 'angular way' is to construct an object which will be data-wise identical to how your table should look like and then you'd use ng-repeat to display the info.
You say you have the city id given in your branch. So you can just construct an object in which you push all your city names:
$scope.tableInfo = [];
for(var i = 0; i < branches.length; i++) {
var tableCell = {
name: branches[i].name,
address: branches[i].address,
city: branches.filter(function(element){
for(var city in cities) {
if(element.cityId === city.id) return city.name;
}
})[0];
};
}
That [0] is there because it'll always be a 1-element array(because of the filter function).
In the HTML, just use a <table> to show the tableInfo object.
<table>
<tr ng-repeat="tableCell in tableInfo">
<td>{{tableCell.name}}</td>
<td>{{tableCell.address}}</td>
<td>{{tableCell.city}}</td>
</tr>
</table>

Displaying data with ng-repeat from JSON array in angular

Hi I have following data that is returned from service which I do not have any control on how it is returned:
{"day_1":[{"classroom":"Nursery","count":0},{"classroom":"Junior Kindy","count":1}],"day_2":[{"classroom":"Nursery","count":4},{"classroom":"Junior Kindy","count":0}]}
but I need to display it in pivot format that is like below:
classroom | day_1 | day_2
============ ======== ======
Nursery | 0 | 4
Junior Kindy | 1 | 0
This is the code in controller
$scope.rolls=[];
Rolls.getRollMarked().then(
function(data){
console.log(data.data);
$scope.rolls = data.data;
}
)
in the view I am using following but it doesn't display any count for the day and I am not sure how to display it..so please let me know how can I display it in the above format?
<table class="table table-bordered">
<tr>
<td>Classroom</td>
<td>day_1</td>
<td>day_2</td>
</tr>
<tr ng-repeat="roll in rolls">
<td>
{{roll[$index]['classroom']}}
</td>
<td>
{{roll.day_1}}
</td>
<td>
{{roll.day_2}}
</td>
</tr>
</table>
You need to convert your data. ng-repeat as you have set it up expects an array.
Using some easy code you can get it to an array though, and then your code will work alright.
Also, you should update your html. You don't need to reference items using $index since each item is bound to the iterator variable in that case
<table class="table table-bordered">
<tr>
<th>Classroom</th>
<th>day_1</th>
<th>day_2</th>
</tr>
<tr ng-repeat="roll in rolls">
<td>
{{roll.classroom}}
</td>
<td>
{{roll.day_1}}
</td>
<td>
{{roll.day_2}}
</td>
</tr>
</table>
And then call a convert function that makes the data into an array. I've used lodash.find here, so you either need to reference that or use your own find method.
Rolls.getRollMarked().then(
function(data){
console.log(data.data);
$scope.rolls = convert(data.data);
}
)
function convert(json) {
var rolls = [];
var days = ['day_1', 'day_2'];
for (var d = 0; d < days.length; ++d) {
var day = days[d];
for (var i = 0; i < json[day].length; ++i) {
var classroom = json[day][i];
var existing = _.find(rolls, { "classroom": classroom.classroom });
if (!existing) {
existing = { classroom: classroom.classroom };
rolls.push(existing);
}
existing[day] = classroom.count;
}
}
return rolls;
}

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.

Refreshing ng-repeat without $scope, without new request to sever

I have a table with this content:
<tbody>
<tr ng-repeat="result in ctrl.result track by $index">
<td ng-bind="$index+1"></td>
<td ng-bind="::result.title"></td>
<td> <button type="button" class="btn" ng-click='ctrl.deleteItem(result)'>Delete</button></td>
</tr>
</tbody>
And in my controller I have:
vm.deleteItem = function (result) {
myService.deleteItem(result.id)
.then(function (response) {
vm.result.splice(result, 1);
});
};
As you see, the vm.result has changed if the item be deleted successfully.
Now, the item deleted in db, so we have response and then the item has been removed from vm.result, too. But the list hasn't updated in browser.
As you see, I use controller as approach and not $scope .
Try deleting the item this way:
vm.result.splice(vm.result.indexOf(result), 1);
The first Splice parameter should be the index of element rather than element itself.
var index = vm.result.findIndex(function(item) {
if (item.id == test.id) {
return true;
}
});
if (index !== -1) {
// Removing the test from list;
vm.result.splice(index, 1);
}
check based on id from the array test.id is the id of the element u r deleting

Resources