BookYourSeat:使用AngularJS [GIF]点击自动座位选择

时间:2016-06-13 18:24:55

标签: javascript html angularjs frameworks logic

BookyourSeat:

这是一款angularJs应用程序,可以帮助您为类似bookmyshow的电影节预订座位。

Seat Selection GIF]

用户可以做什么(用户案例)?

  1. 选择并取消选择相对于selectedVal的座位,即如果 selectedVal = 4然后用户总共只能选择4个席位。

  2. 如果SelectedVal小于1,则用户不应该 除非用户取消选择任何一个,否则选择座位 之前选择的座位并再次选择。

  3. 预订座位案例:如果座位的检查值为真,那么 用户不应该选择或取消选择该座位(a.blocked 为此目的添加了CSS规则),因为它已被选中 另一个用户(让我们假设)。

  4. 自动座位选择案例

      

    如GIF所示

    1. 如果用户选择3个座位并点击第一行中的第一个座位,则应自动在同一行中选择2个和3个。
    2. First Case select Consecutive result

      1. 如果用户选择3个座位并点击该行中的倒数第二个座位,则应填写最后两个座位,并且应在用户点击的任何位置填充剩余座位。
      2. Second Case remaining seat selection case

        1. 如果用户选择3个座位并仅点击最后一个座位,则只能填充该座位。
        2. Last seat selection in the row

          如果有4个席位。

          4 Seats selected but in different rows

          问题:

          我可以使用angular.forEach()实现自动选择过程,但不能正确使用所有逻辑。

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

          工作小提琴:https://jsfiddle.net/rittamdebnath/5vqxgtq3/11/

2 个答案:

答案 0 :(得分:1)

你的循环逻辑有点难以理解,但更关键的是,不必要。它不是循环遍历整个集合的click函数,而是直接与绑定到被单击元素的对象进行交互。

将锚元素绑定更改为:ng-click="clickSeat(item)",并使用控制器上的函数,如下所示:

$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++;
      }
    }
}

这是一个更新的小提琴:https://jsfiddle.net/5vqxgtq3/12/

我不知道这是否会捕获所有您正在寻找的功能,但希望有效地证明逻辑在不再适用时更容易推理依赖于循环,你可以从那里扩展它。

答案 1 :(得分:1)

我已使用以下几点更改了代码以实现逻辑:

  • 将模型更改为2d-Array样式。 seats = [ [Obj0, Obj1, Obj2], [Obj3, Obj4, Obj5] ]
  • 计算到边界的距离=为当前行选择的座位数。然后我们可以计算当前计数的其余可用座位。
  • 添加了rang属性以创建不同行组的部分。

我想我已经实现了所有用例。

请查看下面的演示或fiddle

我的代码也非常复杂,但我认为由于其他选择的更新,循环是必需的。

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.com/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>