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.net/rittamdebnath/5vqxgtq3/11/
Your 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.net/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 complicated 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.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>
Related
I have an array of values that can be cycled through using the next/prev buttons:
Example:
var sav = [
"first item",
"second item",
"third item"
];
var box = document.getElementById('box');
var i = -1;
function next() {
i = i >= sav.length - 1 ? 0 : i + 1;
box.innerHTML = sav[i];
}
function prev() {
i = i > 0 ? i - 1 : sav.length - 1;
box.innerHTML = sav[i];
}
Previous
<div id="box"></div>
Next
Tell me please how to iterate over if the array is multidimensional?
I managed to make an example based on a one-dimensional array, but it does not work in the case of a multidimensional one
The next() and prev() functions just need a little logic to cycle two, dependent variables. The snippet below explains further:
var sav = [
["0-a", "0-b", "0-c"],
["1-a", "1-b", "1-c"],
["2-a", "2-b", "2-c"],
];
const box = document.getElementById('box');
let row = 0, col = 0;
box.innerHTML = sav[row][col];
function next() {
if (col === sav[row].length-1) {
// if the col is at its limit, reset...
col = 0;
// ...and advance the row. if we're at its limit, reset it, too
row = row === sav.length-1 ? 0 : row+1;
} else { // otherwise, just advance the col
col = col+1
}
box.innerHTML = sav[row][col]
}
function prev() {
// same as above, except 0 is the limit, and we subtract to "advance"
if (col === 0) {
col = sav[row].length-1;
row = row === 0 ? sav.length-1 : row-1;
} else {
col = col-1
}
box.innerHTML = sav[row][col]
}
Previous
<div id="box"></div>
Next
Please see my js function below. Inside the display_data function, I call the show_product_block function to show individual product blocks.
var item =[];
function display_data(sort_value){
var my_data = get_data();
var item_price;
if (my_data != null) {
for (i = 0; i < my_data.length; i++) {
var item_name = my_data[i].name;
var item_qty = my_data[i].qty;
var item_price = get_price(my_data[i].innder_id);
item[i] = item_name +'-'+ item_qty +'-'+ item_price;
show_product_block(item_name, item_qty,item_price);
}
}
}
Here I need to call the show_product_block based on price low to high, high to low, and most quantity required. I have a dropdown menu with values default, price_low,price_high, and most_qty. Now somebody changes the dropdown value to most_qty; then I have to call show_product_block based on the most quantity needed. So I modified the function like this.
var item =[];
function display_data(sort_value){
var my_data = get_data();
var item_price;
if (my_data != null) {
for (i = 0; i < my_data.length; i++) {
var item_name = my_data[i].name;
var item_qty = my_data[i].qty;
var item_price = get_price(my_data[i].innder_id);
item[i] = item_name +'-'+ item_qty +'-'+ item_price;
if (sort_value == 'default') {
show_product_block(item_name, item_qty,item_price);
}
}
if (sort_value == 'most_qty'){
*Here from the item array, I have to take the product
based on item_qty and then call show_product_block
inside a for loop.*
}
}
}
Please help me to solve this.
Consider the following.
$(function() {
var myData = [{
name: "Item 1",
qty: 3,
innder_id: 1001
}, {
name: "Item 5",
qty: 2,
innder_id: 1002
}, {
name: "Item 3",
qty: 1,
innder_id: 1003
}];
function obj_sort(o, i) {
// Input: Object, Index
// Output: Sorted Object by Index
if (o == undefined) {
// Object is undefined, exit
console.log("Object Undefined");
return false;
}
// Collect object keys
var k = Object.keys(o[0]);
if (i === undefined) {
// If Index is undefined, set to first Key
i = k[0];
}
if (typeof i === "number" && i < k.length) {
// If Index is a number and in range, select proper Key
i = k[i];
} else {
console.log(i);
return -1;
}
if (k.indexOf(i) === -1) {
console.log(i + " is not in Range");
return -1;
}
// Sort the Object by Index
o = o.sort(function(a, b) {
// Check for Number Sort
if (typeof a[i] === "number") {
return a[i] - b[i];
}
// Text Sort
if (a[i] < b[i]) {
return -1;
}
if (a[i] > b[i]) {
return 1;
}
return 0;
});
// Return the sorted Object
return o;
}
function showData(o, t) {
console.log("Show Data: " + o.length + " Rows");
$("tbody", t).html("");
$.each(o, function(i, row) {
var tr = $("<tr>").appendTo($("tbody", t));
$.each(row, function(j, cell) {
$("<td>").html(cell).appendTo(tr);
});
});
}
showData(myData, $(".table-wrapper table"));
$("table thead th").click(function() {
myData = obj_sort(myData, $(this).index());
showData(myData, $(".table-wrapper table"));
});
});
.table-wrapper table {
width: 100%;
}
.table-wrapper table thead th {
cursor: pointer;
}
.table-wrapper table tbody td {
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Name</th>
<th>Quantity</th>
<th>Price</th>
</thead>
<tbody>
</tbody>
</table>
</div>
You can use Sort, yet you need a comparison function.
Specifies a function that defines the sort order. If omitted, the array elements are converted to strings, then sorted according to each character's Unicode code point value.
See more: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
I've provided an example of a highly dynamic sort function. You pass in an Object, and, optionally, an Index as parameters. It will return an object that is sorted.
This example allows you to click the headers to sort by that column. It will always default to an ascending sort order.
I want to create a star rating system that has 5 stars. You can not select a half star, only a whole one. I want to achieve the following: If the user clicks on the star, the cilcked one and the other before it should be activated, and if the user clicks on a lower star deactivate all the stars after the selected one.
Here is what I got so far: The user can select 4 stars out of five (on the fifth click I have a bug which should be solved).
PS: I am working with SVG images but it would be way too ugly to insert in so the [ ] are the empty stars (the default), and the [X] are the selected (active) stars.
Heres my code:
for (let i = 1; i <= 5; i++) { document.getElementById("w__stars").innerHTML += `<span class="r__icon">[ ]</span>`; }
var icon = document.getElementsByClassName("r__icon");
for (let i = 0; i < icon.length; i++) {
icon[i].addEventListener("click", function (e) { console.log("--");
for (let j = 0; j < i+1; j++) {
console.log("i: " +i); console.log("j: "+(j+1)); console.log("Rest: "+ (j+(5-(i+1))));
icon[j].innerHTML = `[X]`;
icon[i+(5-(i+1))].innerHTML = `[ ]`;
}
});
}
<div class="flex flex-row product-star-con" id="w__stars"></div>
Your method just needs a different approach. For instance that inner loop is unnecessary if you are to place this in there icon[j].innerHTML = '[X]'.. which can be placed just within the outer loop.
Also the unnecessary calculations are making the task seem harder than it actually is. And since this is a loop, the i variable will always have the highest value within the loop, since there is no break statement in there
The method below targets the next elements and previous elements relative to the one being clicked at the moment and applies the appropriate 'innerHTML' to them
// Function to get previous and next siblings of the target element
function getSiblings(element, type){
var arraySib = [];
if ( type == 'prev' ){
while ( element = element.previousSibling ){
arraySib.push(element);
}
} else if ( type == 'next' ) {
while ( element = element.nextSibling ){
arraySib.push(element);
}
}
return arraySib;
}
for (var i = 1; i <= 5; i++) { document.getElementById("w__stars").innerHTML += `<span class="r__icon">[ ]</span>`; }
var icon = document.getElementsByClassName("r__icon");
for (var i = 0; i < icon.length; i++) {
icon[i].addEventListener("click", function (e){
this.innerHTML = `[X]`;
var prev = getSiblings(this, 'prev')
var next = getSiblings(this, 'next')
// populate previous siblings
for(p = 0; p < prev.length; p++){
prev[p].innerHTML = `[X]`
}
// clear next siblings
for(n = 0; n < next.length; n++){
next[n].innerHTML = `[]`
}
});
}
<div class="flex flex-row product-star-con" id="w__stars"></div>
Another approach:
// Setting stars
const stars = [];
for (let i = 0; i <= 4; i++) {
stars.push({
active: false,
index: i
});
}
const renderStars = (parentElement, stars, activeContent, notActiveContent) => {
parentElement.innerHTML = '';
stars.forEach(({ active, index }) => {
parentElement.innerHTML += `
<span class="r__icon">${active ? activeContent : notActiveContent}</span>`;
});
Array.from(parentElement.querySelectorAll('.r__icon')).forEach((item, itemIndex) => {
const star = stars.find(({ index }) => index === itemIndex);
stars[star.index].element = item;
item.addEventListener('click', (e) => {
const itemElement = e.target;
const starIndex = stars.findIndex(({ element }) => element === itemElement);
if (starIndex === -1) {
return;
}
const toActive = stars[starIndex].active !== true;
stars = stars.map(star => {
if (toActive) {
// Set items before index to true, and after to false
if (star.index <= starIndex) {
return {
...star,
active: true
};
}
return {
...star,
active: false
};
} else {
// Set items after index to false, and before to true
if (star.index >= starIndex) {
return {
...star,
active: false
};
}
return {
...star,
active: true
};
}
});
renderStars(parentElement, stars, activeContent, notActiveContent);
});
});
};
const setupStars = (stars, activeContent, notActiveContent) => {
const parentElement = document.getElementById("w__stars");
if (!parentElement) {
return;
}
renderStars(parentElement, stars, activeContent, notActiveContent);
};
setupStars(stars, '[X]', '[ ]');
<div class="flex flex-row product-star-con" id="w__stars"></div>
I am new to Angular. Here I am trying to format JSON data being displayed on a table. Here is what I am trying to achieve, for empty rows, instead of being blank , I fill them with a hyphen, values greater than 25 I fill there individual cells background with red color, values between 20 and 25 i fill them with yellow color, but for some weird reason my table is not being formatted completely, it is like it is not detecting the ngAfterViewInit() function, also when i try to get the average of all the columns i am getting the result as NaN for all columns.
My component.ts file
#ViewChild('myTable', {static: false}) myTable: ElementRef;
ngAfterViewInit() {
var table = this.myTable.nativeElement;
var row;
var cell;
var j = 1;
var r = 0;
var t = 0;
//console.log(table);
if (table !== null) {
while (row = table.rows[r++]) {
var c = 0;
while (cell = row.cells[c++]) {
var cellValue = cell.innerHTML;
//cell.innerHTML='[Row='+r+',Col='+c+']'; // do sth with cell
if (c === 5 && cellValue > 25) {
cell.style.background="#FF0000";
} else if (c === 5 && cellValue >= 10 && cellValue < 25) {
cell.style.background="#FFFF00";
}
}
}
//getting the average value of each column
var colL = table.rows.length - 1;
//console.log(table.rows[1].cells.length);
for (j = 1; j < table.rows[1].cells.length; j++) {
var sumVal = 0;
for (var i = 1; i < table.rows.length; i++) {
if (i < colL) {
//console.log(table.rows[i].cells[1].innerHTML);
if (Number.isInteger(parseInt(table.rows[i].cells[j].innerHTML)) === true) {
sumVal = sumVal + parseFloat(table.rows[i].cells[j].innerHTML);
} else {
table.rows[i].cells[j].value = "-";
table.rows[i].cells[j].style.backgroundColor = "#FFFF00";
}
}
// console.log(table.rows[colL].cells[table.rows.length - 1].innerHTML);
}
//Setting the last cell with the avirrage value
if (table.rows[colL].cells[j] !== table.rows[colL].cells[table.rows[1].cells.length - 2]) {
var ans = (sumVal / (table.rows.length - 2)).toFixed(2);
table.rows[colL].cells[j].innerHTML = ans;
} else
table.rows[colL].cells[j].innerHTML = sumVal;
//Taking out all cells with zore totals
if (parseFloat(ans) === 0) {
for (t = 0; t <= colL; t++) {
table.rows[t].cells[j].style.display = 'none';
}
}
}
}
}
}
My beginning of the Table
<div class="panel-body" >
<div class="table-responsive">
<table class="table table-hover" #myTable>
<thead>
My beginning of the rows
<tr class="even gradeC" style="text-align:center" ng-repeat="home in homeurban"
*ngFor="let home of homeurban">
<td class="infos" style="position: absolute; top: auto; width: 100.5px;">{{home.teamNumber}}</td>
What I am doing wrong?
Thanks in advance
I have set filter in Kendo grid but i have a problem when filter applied to grid, i missed value of my filter row.
After filter i missed my filter :
Now for this reason, i set my filter row again so bellow code :
function updateSearchFilters(grid, field, operator, value)
{
var newFilter = { field: field, operator: operator, value: value };
var dataSource = grid.dataSource;
var filters = null;
if ( dataSource.filter() != null)
{
filters = dataSource.filter().filters;
}
if ( filters == null )
{
filters = [newFilter];
}
else
{
var isNew = true;
var index = 0;
for(index=0; index < filters.length; index++)
{
if (filters[index].field == field)
{
isNew = false;
break;
}
}
if ( isNew)
{
filters.push(newFilter);
}
else
{
//alert(value);
if(value == '')
filters.splice(index,1);
//delete filters[index];
else
filters[index] = newFilter;
}
}
dataSource.filter(filters);
for (var i = 0; i < filters.length; i++) {
$('#gridId-filter-column-' + filters[i].field.toString()).val(filters[i].value.toString());
}
}
When i set the break point in this line $('#gridId-filter-column-' + filters[i].field.toString()).val(filters[i].value.toString()); it worked correct but
when i remove break point this line doesn't work.
you can set delay before run this line :
for (var i = 0; i < filters.length; i++) { $('#gridId-filter-column-' +filters[i].field.toString()).val(filters[i].value.toString()); }