BookyourSeat:
It is an angularJs app that helps you book your seats for a movie show similar to bookmyshow .
]
What the User Can Do (User Cases)?
Select and Deselect the Seats with respect to the selectedVal, i.e if the selectedVal = 4 then the user can select only 4 seats in total.
if the SelectedVal is less than 1 then the user should not be able to select the seat anymore unless the user deselect any of the previously selected seats and select again.
Booked Seats Case: If the check value of a seat is true, then the user should not be able to select or deselect that seat(a.blocked CSS rule is Added for that purpose) since it is already selected by another user(Lets assume).
Automatic Seat Selection Cases
As shown in the GIF
- If the user selects 3 seats and click on the first seat in the first row it should automatically select 2 and 3 on the same row.
- If the user Selects 3 seats and clicks on the second last seat in the row then last two seats should be filled and the remaining seat should be filled where ever the user clicks.
- If the user selects 3 seats and clicks on only the last seat then only that seat should be filled.
In case of 4 seats.
Problem:
I am able to achieve the automatic selection process using angular.forEach()
but cannot all the logics correctly.
$scope.execute = function(i, j, itemVal, itemLetter) {
angular.forEach($scope.obj, function(v, k) {
if (v[i].val == itemVal && v[i].letter == itemLetter) {
if (v[i].seat == true || ($scope.isDisabled && v[i].check == false)) {
return;
}
v[i].check = !v[i].check;
if (v[i].check)
$scope.selectedVal -= 1;
else
$scope.selectedVal += 1;
//seatSelection
var m = i;
for (var l = 0; l < $scope.selectedVal; l++)
v[m++].check = true;
//seatSelectionEnd
console.log(itemVal + " " + itemLetter);
if ($scope.selectedVal < 1) {
$scope.isDisabled = true;
} else {
$scope.isDisabled = false;
}
}
});
};
}])
Working Fiddle: /
BookyourSeat:
It is an angularJs app that helps you book your seats for a movie show similar to bookmyshow .
]
What the User Can Do (User Cases)?
Select and Deselect the Seats with respect to the selectedVal, i.e if the selectedVal = 4 then the user can select only 4 seats in total.
if the SelectedVal is less than 1 then the user should not be able to select the seat anymore unless the user deselect any of the previously selected seats and select again.
Booked Seats Case: If the check value of a seat is true, then the user should not be able to select or deselect that seat(a.blocked CSS rule is Added for that purpose) since it is already selected by another user(Lets assume).
Automatic Seat Selection Cases
As shown in the GIF
- If the user selects 3 seats and click on the first seat in the first row it should automatically select 2 and 3 on the same row.
- If the user Selects 3 seats and clicks on the second last seat in the row then last two seats should be filled and the remaining seat should be filled where ever the user clicks.
- If the user selects 3 seats and clicks on only the last seat then only that seat should be filled.
In case of 4 seats.
Problem:
I am able to achieve the automatic selection process using angular.forEach()
but cannot all the logics correctly.
$scope.execute = function(i, j, itemVal, itemLetter) {
angular.forEach($scope.obj, function(v, k) {
if (v[i].val == itemVal && v[i].letter == itemLetter) {
if (v[i].seat == true || ($scope.isDisabled && v[i].check == false)) {
return;
}
v[i].check = !v[i].check;
if (v[i].check)
$scope.selectedVal -= 1;
else
$scope.selectedVal += 1;
//seatSelection
var m = i;
for (var l = 0; l < $scope.selectedVal; l++)
v[m++].check = true;
//seatSelectionEnd
console.log(itemVal + " " + itemLetter);
if ($scope.selectedVal < 1) {
$scope.isDisabled = true;
} else {
$scope.isDisabled = false;
}
}
});
};
}])
Working Fiddle: https://jsfiddle/rittamdebnath/5vqxgtq3/11/
Share Improve this question asked Jun 13, 2016 at 18:24 rittamrittam 3181 gold badge3 silver badges14 bronze badges2 Answers
Reset to default 1Your looping logic is a bit difficult to follow, but more critically, unnecessary. Instead of a click function that loops through the whole collection, have it just interact directly with the object bound to the element being clicked.
Change your anchor element binding to this: ng-click="clickSeat(item)"
, and use a function on the controller like this:
$scope.clickSeat = function(seat) {
if (!seat.seat && !$scope.isDisabled) {
if (seat.check) {
seat.check = false;
$scope.selectedSeatCount--;
} else if ($scope.selectedSeatCount < $scope.selectedVal) {
seat.check = true;
$scope.selectedSeatCount++;
}
}
}
Here's an updated fiddle: https://jsfiddle/5vqxgtq3/12/
I don't know if this captures all the functionality you're looking for, but hopefully effectively demonstrates how the logic is easier to reason when it's no longer relying on a loop, and you can expand upon it from there.
I've changed your code with the following points to implement the logic:
- changed model to 2d-Array style.
seats = [ [Obj0, Obj1, Obj2], [Obj3, Obj4, Obj5] ]
- calculated distance to border = how many seats to select for current row. Then we can calculate the rest of the available seats for current count.
- added
rang
property to create the sections for different row groups.
I think I've implemented all your use cases.
Please have a look at the demo below or at this fiddle.
My code is pretty plicated as well but I think the loops are required because of the updating of the other selections.
angular.module('bookYourSeatApp', [])
.factory('seats', SeatsFactory)
.controller('mainCtrl', MainCtrl);
function SeatsFactory($rootScope, $timeout) {
var seatProps = {
id: 0,
caption: 0,
checked: false,
booked: false
};
var seats = {
'firstRang': {
// col0 1 2 3 4 5
// row 0 seat 0 1 2 3 4 5
// row 1 seat 6 7 8 9 10 11
seats: createSeats(2, 6) // rows, cols
},
'secondRang': {
seats: createSeats(3, 6)
}
};
function createSeats(rows, cols) {
var arr = [[]];
var seatIndex = 0;
for (var row = 0; row < rows; row++) {
arr[row] = [];
for(var col=0; col < cols; col++) {
var seat = angular.extend({}, seatProps, {
id: seatIndex,
caption: seatIndex,
booked: seatIndex < 5 // 0 to 5 booked
});
arr[row][col] = seat;
seatIndex++;
}
}
return arr;
}
function checkSelected(newCount) {
// selected fewer or more than persons in select.
// --> uncheck all
var checkedCount=0, keys = Object.keys(seats);
for (var rang=0; rang < keys.length; rang++) {
var key = keys[rang];
var curSeats = seats[key].seats;
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
if ( curSeats[row][col].checked ) {
checkedCount++;
}
}
}
//console.log('new count', newCount, checkedCount);
// we can have more or less selections after selection change
// --> more inc availCount
if (checkedCount === 0) {
// nothing selected
factory.availCount = angular.copy(newCount);
}
else if (newCount.val > checkedCount) {
//console.log('add delta', newCount, checkedCount)
factory.availCount.val = (newCount.val - checkedCount);
} else {
removeAllCheck();
}
}
}
function removeCheck(rang) {
// later pass user to this function (for now remove all checked)
/*var curSeats = seats[rang].seats
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
curSeats[row][col].checked = false;
}
}*/
keys = Object.keys(seats);
for (var rang=0; rang < keys.length; rang++) {
var key = keys[rang];
var curSeats = seats[key].seats;
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
curSeats[row][col].checked = false;
}
}
}
}
function removeAllCheck() {
keys = Object.keys(seats);
for (var rang=0; rang < keys.length; rang++) {
var key = keys[rang];
var curSeats = seats[key].seats;
for (var row=0; row < curSeats.length; row++) {
for (var col=0; col < curSeats[row].length; col++) {
curSeats[row][col].checked = false;
}
}
}
}
function selectSeats(selection, count) {
// todo:
// check distance to border, keep the rest as clickable
// selection = {rang, row, seat}
console.log(selection);
var row = selection.row,
seat = selection.seat;
if ( !seat.booked ) {
//console.log('availCount', factory.availCount);
if ( factory.availCount.val == 0 ) {
//console.log('new selection');
factory.availCount = angular.copy(count);
removeCheck(); //selection.rang);
}
var borderDistance = row.length - row.indexOf(seat),
rest = borderDistance > count.val ? 0: count.val - borderDistance;
if ( factory.availCount.val === count.val) {
// first click
var lastIndex = rest > 0 ? row.length: row.indexOf(seat) + count.val;
for ( var seatIndex = row.indexOf(seat); seatIndex < lastIndex; seatIndex++) {
row[seatIndex].checked = true;
}
factory.availCount.val = rest; // update available seats
}
else {
// second click dec. availCounter
// single change of seats
/*if ( factory.availCount.val < 0 ) {
row[row.indexOf(seat)].checked = false; // remove check
factory.availCount.val++;
}
else {*/
if ( !row[row.indexOf(seat)].checked ) {
// only if not already checked
row[row.indexOf(seat)].checked = true;
if ( factory.availCount.val > 0 ) {
factory.availCount.val--;
}
}
//}
}
}
}
var factory = {
map: seats,
select: selectSeats,
availCount: {},
setAvailCount: function(count) {
console.log('avail', count);
checkSelected(count);
}
};
return factory
}
function MainCtrl(seats) {
var vm = this;
angular.extend(vm, {
seats: seats,
selectionCount: [//[0,1,2,3,4],[
{id: 0, val: 0}, // object for two-way binding
{id: 1, val: 1},
{id: 2, val: 2},
{id: 3, val: 3},
{id: 4, val: 4},
],
selectedCount: 0
});
vm.selectedCount = vm.selectionCount[2];
seats.setAvailCount(vm.selectedCount);
}
table {
border: 1px solid black;
padding: 0.5em;
}
td {
padding: 1em;
border: 2px solid gray;
}
td:hover {
cursor: default;
background-color: gray;
}
.active {
border: 2px solid lightgreen;
border-radius: 5px;
}
.booked {
background-color: lightgray;
}
<script src="https://ajax.googleapis./ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="bookYourSeatApp" ng-controller="mainCtrl as ctrl">
<label>Persons <select ng-model="ctrl.selectedCount" ng-change="ctrl.seats.setAvailCount(ctrl.selectedCount)" ng-options="count as count.val for count in ctrl.selectionCount"></select></label>
Seats left: {{ctrl.seats.availCount.val}}<br/>
<table ng-repeat="(key, rang) in ctrl.seats.map">
<tr ng-repeat="row in rang.seats">
<td ng-repeat="seat in row" ng-class="{'active': seat.checked, 'booked': seat.booked}" ng-click="ctrl.seats.select({rang:key, row:row, seat: seat}, ctrl.selectedCount)">
{{seat.caption}}
</td>
</tr>
</table>
<pre>{{ctrl.seats.map | json : 2}}</pre>
</div>