I'm new to angualr, and developing some application on angular.
I have a table where the rows will be added dynamically, and pagination is used to traverse through the table. I have used 5 rows per page and selection of these rows per page can be selected by a dropdown.
Problem Facing: When i click on add button, each time it adds the row. After adding 5 rows if i click on add button then 2nd page pagination will be displayed.
But the rows in the second page contains the data of first page only.
If I select the dropdown to show to items the 6th item will be empty.
The 6th text box under pagination of page 2 selects the value of 1st input of first page.
Please help me to solve this pagination issue.
TypeScript code:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormArray, FormGroup } from '#angular/forms';
import { SelectItem } from 'primeng/api';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
createOrderForm: FormGroup;
isAllPortTnsSelected: boolean;
tablePaginationDropDowns: SelectItem[];
tableRowsToBeDisplayed: number;
totalTns: number;
constructor(private formBuilder: FormBuilder) {
this.tableRowsToBeDisplayed = 5;
this.totalTns = 0;
}
ngOnInit() {
this.loadTablePaginationDropDowns();
//load the form with all form controls.
this.createOrderForm = this.formBuilder.group({
tnList: this.formBuilder.array([]),
tnListDropDown: ['']
});
// any change in the porting tn table pagination dropdown, it should reflect the value
this.createOrderForm.get('tnListDropDown').valueChanges.subscribe(
data => {
this.changePortingTnDropDownValue(data);
}
)
}
loadTablePaginationDropDowns() {
this.tablePaginationDropDowns = [
{ label: "5", value: "five" },
{ label: "10", value: "ten" },
{ label: "15", value: "fifteen" },
{ label: "20", value: "twenty" }
]
}
addTnGroup(): FormGroup {
return this.formBuilder.group({
tnCheckBox: [''],
fromTn: [''],
toTn: ['']
});
}
addTnRow() {
//get the reference for the portingTnList and add one more set of tnGroup
// before adding the tn group we need to typecast the control to FormArray
(<FormArray>this.createOrderForm.get('tnList')).push(this.addTnGroup());
this.totalTns = (<FormArray>this.createOrderForm.get('tnList')).length;
}
removeTnRow(group: FormArray = <FormArray>this.createOrderForm.get('tnList')) {
const tempArray: FormArray = new FormArray([]);
Object.keys(group.controls).forEach (
(key: string) => {
const abstractControl = group.get(key);
if(abstractControl instanceof FormGroup) {
if (!this.removeTnRowIfSelected(abstractControl)) {
tempArray.push(abstractControl);
}
}
}
);
while(group.length != 0) {
group.removeAt(0);
}
while(tempArray.length != 0) {
group.push(tempArray.at(0));
tempArray.removeAt(0);
}
this.totalTns = group.length;
}
removeTnRowIfSelected(group: FormGroup): boolean{
if (group.get('tnCheckBox') && group.get('tnCheckBox').value !== '') {
return true;
} else {
return false;
}
}
selectAllTns() {
this.selectAndDisSelectAllTns();
this.isAllPortTnsSelected = !this.isAllPortTnsSelected;
}
selectAndDisSelectAllTns(group: FormArray = <FormArray>this.createOrderForm.get('tnList')) {
Object.keys(group.controls).forEach(
(key: string) => {
const abstractControl = group.get(key);
if (abstractControl instanceof FormGroup) {
this.selectAndDisSelectTnsInGroup(abstractControl);
}
}
)
}
selectAndDisSelectTnsInGroup(group: FormGroup) {
if (!this.isAllPortTnsSelected) {
group.get('tnCheckBox').setValue('select');
} else {
group.get('tnCheckBox').setValue('');
}
}
AddBulkTns() {
console.log(this.createOrderForm.get('tnList'));
}
/**
* Method changeDropDownValue() used to reflect the changes done for the dropdown values which are used to
* decide the number of rows to be displayed in the table
*
* #param data : Accepts the string value of dropdown
*/
changePortingTnDropDownValue(data: string) {
if (data === 'ten') {
this.tableRowsToBeDisplayed = 10;
} else if (data === 'fifteen') {
this.tableRowsToBeDisplayed = 15;
} else if (data === 'twenty') {
this.tableRowsToBeDisplayed = 20;
} else {
this.tableRowsToBeDisplayed = 5;
}
}
}
HTML Code:
<h3>Dynamic</h3>
<form [formGroup]="createOrderForm">
<div class="div-grid">
<div style="width: 100%;">
<div class="top-div-checkbox">
<input class="form-check-input position-static" type="checkbox" style="margin-left: 30%; width: auto"
(click)="selectAllTns()">
</div>
<div class="top-div-label1">
<label>From TN</label>
</div>
<div class="top-div-label2">
<label>To TN</label>
</div>
</div>
<div id="gridDiv">
<table class="test">
<tbody formArrayName="tnList">
<tr [formGroupName]="j"
*ngFor="let tnGroup of createOrderForm.get('tnList').controls |
paginate:{itemsPerPage: tableRowsToBeDisplayed, currentPage: page}; let j = index">
<td class="td-checkbox">
<input type="checkbox" formControlName="tnCheckBox" [id]="'tnCheckBox'+j">
</td>
<td class="td-input1">
<input type="text" formControlName="fromTn" [id]="'fromTn'+j">
</td>
<td class="td-input2">
<input type="text" formControlName="toTn" [id]="'toTn'+j">
</td>
</tr>
</tbody>
</table>
</div>
<div class="nav-div">
<div class="pagination-div">
<pagination-controls previousLabel="" nextLabel="" (pageChange)="page = $event"></pagination-controls>
</div>
<div class="page-dropdown-div">
<select name="tnListPageDropDown" id="tnListPageDropDown" class="custom-select mr-sm-2"
formControlName="tnListDropDown" style="width: 60px">
<option value="" disabled selected>5</option>
<option *ngFor="let dropDown of tablePaginationDropDowns" [value]="dropDown.value">{{dropDown.label}}</option>
</select>
<label> items per Page</label>
</div>
<div class="total-items-div">
Total {{totalTns}} items
</div>
</div>
<div>
<button type="button" class="btn btn-info list-btns" (click)="addTnRow()">Add</button>
<button type="button" class="btn btn-info list-btns" (click)="removeTnRow()">Remove</button>
<button type="button" class="btn btn-info list-btns" (click)="AddBulkTns()">Bulk Add</button>
</div>
</div>
</form>
StyleSheet
.test {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
table-layout:fixed;
border: 1px solid #ddd;
}
div {
border: 1px solid #ddd;
}
.test th, .test td {
border: 1px solid #ddd;
padding: 8px;
}
.test tr:nth-child(even) {
background-color: #f2f2f2;
}
#gridDiv {
width: 100%;
overflow-y:auto;
height: 200px;
border: 1px solid black;
}
.div-grid {
border: 1px solid black;
width: 50%;
}
td input {
width: 100%;
}
.td-checkbox {
width: 7%;
}
.td-input1{
width: 60%;
}
.td-input2{
width: 33%;
}
.top-div-checkbox {
width: 7%;
border: 1px solid #ddd;
float: left;
}
.top-div-label1 {
width: 60%;
border: 1px solid #ddd;
float: left;
}
.top-div-label2 {
width: 33%;
border: 1px solid #ddd;
float: left;
}
.nav-div {
border: 1px solid #ddd;
height: 48px;
}
.pagination-div {
width: 50%;
float: left;
}
.page-dropdown-div {
width: 30%;
float: left;
}
.total-items-div {
width: 20%;
float: left;
}
.list-btns {
padding-left: 5%;
padding-right: 5%;
margin-right: 10%;
margin-left: 2%;
}
Related
I have currently figured out a way to store value of 6am plan in a content editable box in my local storage key
item 2
How do I make this happen for all the other hours like 1
work plan for every hour of the day 6 am - 11pm
storing input in one key
using this code snippet below
javascript -
var content = document.getElementById('content'),
address = document.getElementById('address'),
saveButton = document.getElementById('save'),
loadButton = document.getElementById('load'),
clearButton = document.getElementById('clear'),
resetButton = document.getElementById('reset');
var localStore = {
saveLocalStorage: function() {
localStorage.setItem('item', content.innerHTML);
},
loadLocalStorage: function() {
var contentStored = localStorage.getItem('item');
if ( contentStored ) {
content.innerHTML = contentStored;
}
},
clearLocalStorage: function() {
localStorage.removeItem('item');
}
};
saveButton.addEventListener('click', function() {
localStore.saveLocalStorage();
}, false);
<r class="notion-table-row">
<td
style="color: inherit; fill: inherit; border: 1px solid gb(233, 233, 231); position: relative; vertical-align: top; min-width: 122px; max-width: 122px; min-height: 32px;">
<div class="notion-table-cell">
<div class="notion-table-cell-text"
spellcheck="true" placeholder=" "
data-content-editable-leaf="true"
style="max-width: 100%; width: 100%; white-space: pre-wrap; word-break: break-word; caret-colour: gb(55, 53, 47); padding: 7px 9px; background-colour: transparent; font-size: 14px; line-height: 20px;"
content editable="false">11 PM</div>
</div>
</td>
<td
style="color: inherit; fill: inherit; border: 1px solid gb(233, 233, 231); position: relative; vertical-align: top; min-width: 200px; max-width: 200px; min-height: 32px;">
<div class="notion-table-cell">
<div class="notion-table-cell-text"
spellcheck="true" placeholder=" "
data-content-editable-leaf="true"
style="max-width: 100%; width: 100%; white-space: pre-wrap; word-break: break-word; caret-colour: gb (55, 53, 47); padding: 7px 9px; background - colour: transparent; font-size: 14px; line-height: 20px;"
<section id="11pm_input" content editable="true"></div>
Store all of the data in an array. Keep in mind, localStorage stores only strings so anything not a string (ex. array, object, number, etc.), must be converted into a string when saved to localStorage:
localStorage.setItem("String", JSON.stringify([...Array]))
and when it is retrieved from localStorge it needs to be parsed into it's original type:
const data = JSON.parse(localStorage.getItem("String"));
In the example below, the data is gathered from all .cell which comprise of the following HTML elements from the table head and body (in ex. 4 x time, 6 x data):
<time class='cell' datetime="23:00">11 PM</time>
<data class='cell' value="0ne Zer0">Zer0 0ne</data>
time.datetime = "23:00";
time.textContent = "11 PM";
data.value = "0ne Zer0";
data.textContent = "Zer0 0ne";
lStorage = [
["23:00", "11 PM"],
["0ne Zer0", "Zer0 0ne"],
["00:00", "12 AM"],
...[N, N]
];
The event handler manageData(event) delegated all click events triggered on the table head, body, and foot.
Variables:
key is whatever the user typed in .input, the value will be assigned to data being saved to localStorage.
data is declared for data coming from and to localStorage.
cells is an array of all time.cell and data.cell tags within the table head and body.
node is determined by event.target property which always refers to the tag the user actually clicked. This reference will be delegated to react according to the .matches() method and a series of flow control statements (if, if else, and else).
The process for loading data from localStorage involves loadData(key) and iteration of the array of arrays saved in localStorage and the array of .cells:
if (node.matches('.load')) {...
...
data = loadData(key);
cells.forEach((n, i) => {
n.textContent = data[i][1];
if (n.matches('time')) {
n.datetime = data[i][0];
} else {
n.value = data[i][0];
}
});
The rest of the code is of simular fashion -- here's a brief description of what it can do:
Load data from key in localStorage and popualte a <table> with it.
Save data from a <table> to a key in localStorage.
Reset data the user typed in the fields (if given a selector, it can reset it as well).
Clear data by key and all data in <table> (including static data).
All .cells are editable (including headers). Double click any .cell within the table head or body to toggle it in/out of edit mode.
Due to SO security policy, WebStorage API will not function, go to: Plunker
The link doesn't work for me when clicked but
I managed to use the url by copy & paste
to the address bar:
https://run.plnkr.co/preview/ckz3pfkfe0007386nlancnzqk/
If the links above don't work, you can copy & paste the code in the Snippet with a text editor and save the file with a *.html extension.
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
:root {
font: 1ch/1.5 'Segoe UI';
}
body {
font-size: 2ch;
}
table {
table-layout: fixed;
width: 70vw;
margin: 20px auto;
}
th {
width: 50%;
font-size: 2.25rem;
}
td {
vertical-align: center;
}
.box {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
min-height: 32px;
padding: 8px 8px 5px;
}
.cell {
display: block;
max-width: 100%;
width: 100%;
min-height: 25px;
white-space: pre-wrap;
word-break: break-word;
font-size: 2rem;
border: 2px solid #000;
background: transparent;
text-align: center;
}
.head {
width: 97.5%;
border-color: transparent;
}
.edit {
border-color: blue;
}
button {
display: block;
font: inherit;
font-size: 2rem;
background: transparent;
border-radius: 6px;
cursor: pointer;
}
button:hover {
border-color: blue;
color: blue;
background: #ddd;
}
.ctrl {
position: relative;
flex-flow: row nowrap;
justify-content: space-between;
min-width: 92%;
height: 30px;
margin-top: 40px;
}
.input {
position: absolute;
top: -40px;
left: -0.5vw;
width: 99%;
height: 25px;
}
.noKey {
color: tomato;
font-weight: 900;
border-color: tomato;
}
.noKey::before {
content: 'Enter the key to data';
}
.done {
color: blue;
border-color: blue;
}
.done::before {
content: 'Data is saved under key: "';
}
.done::after {
content: '"';
}
</style>
</head>
<body>
<main>
<section>
<table>
<thead>
<tr>
<th>
<data class="cell head" value="Hour">Hour</data>
</th>
<th>
<data class="cell head" value="Employee">Employee</data>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<fieldset class="box">
<time class="cell">8 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">9 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">10 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">11 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<fieldset class="ctrl box">
<data class="cell input edit" contenteditable></data>
<button class="load" title="Load data">
Load</button>
<button class="save" title="Save data">
Save</button>
<button class="reset" title="Reset fields">
Reset</button>
<button class="clear" title="Clear saved data and Reset fields">
Clear</button>
</fieldset>
</td>
</tr>
</tfoot>
</table>
</section>
</main>
<script>
const tab = document.querySelector('table');
const hdr = tab.tHead;
const mid = tab.tBodies[0];
const ftr = tab.tFoot;
const cls = ['noKey', 'done'];
const inp = document.querySelector('.input');
const formatTime = time => {
const T = time.split(' ');
return T[1] === 'PM' ? `${+T[0]+12}:00` : `${T[0]}:00`;
};
const lastFirst = name => name.split(' ').reverse().join(', ');
const loadData = key => JSON.parse(localStorage.getItem(key));
const saveData = (key, data) => localStorage.setItem(key, JSON.stringify(data));
const resetData = selector => {
let nodes = selector === undefined ? '.edit' : selector;
[...document.querySelectorAll(nodes)].forEach(n => {
n.textContent = '';
if (n.matches('time')) {
n.datetime = '';
} else {
n.value = '';
}
});
inp.classList.remove(...cls);
};
const clearData = key => {
resetData('.cell');
inp.classList.remove(...cls);
localStorage.removeItem(key);
};
const manageData = e => {
let key = inp.textContent;
let data;
const cells = [...document.querySelectorAll('.cell')];
const node = e.target;
if (node.matches('.load')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
data = loadData(key);
cells.forEach((n, i) => {
n.textContent = data[i][1];
if (n.matches('time')) {
n.datetime = data[i][0];
} else {
n.value = data[i][0];
}
});
}
} else if (node.matches('.save')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
data = cells.flatMap(n => {
if (n.matches('time')) {
n.datetime = formatTime(n.textContent);
return [
[n.datetime, n.textContent]
];
} else {
n.value = lastFirst(n.textContent);
return [
[n.value, n.textContent]
];
}
});
inp.classList.add('done');
saveData(key, data);
}
} else if (node.matches('.reset')) {
resetData();
} else if (node.matches('.clear')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
clearData(key);
}
} else if (node.matches('.input')) {
node.textContent = '';
node.classList.remove(...cls);
} else {
return;
}
};
ftr.onclick = manageData;
const toggleEdit = e => {
const node = e.target;
if (node.matches('.cell')) {
node.classList.toggle('edit');
node.toggleAttribute('contenteditable');
}
};
hdr.ondblclick = toggleEdit;
mid.ondblclick = toggleEdit;
</script>
</body>
</html>
$(function () {
var people = [];
var ctCells = [], questionCells = [], userCells = [];
var $tBody = $("#userdata tbody");
$.getJSON('https://api.myjson.com/bins/18g7fm', function (data) {
$.each(data.ct_info, function (i, f) {
ctCells.push(`<td id=${f.id}>${f.id}</td><td>${f.name}</td>`);
var users = []
var question = []
f.Qid_info.forEach((x) => {
x.user_info.forEach((y) => {
//avoid duplicates
var foundUser = users.find((user) => {
return user.id === y.id
})
if (!foundUser) {
users.push(y)
}
})
})
f.Qid_info.forEach((x) => {
var foundQuestion = question.find((questions) => {
return questions.id === x.id
})
if (!foundQuestion) {
question.push(x)
}
})
$.each(question, function (i, question) {
ctCells.push(`<td colspan="2"> </td>`)
questionCells.push(`<td id=${question.id}>${question.id}</td><td>${question.isActive}</td><td>${question["is complex"]}</td><td>${question["is breakdown"]}</td>`);
})
$.each(users, function (i, user) {
var a = user.data.map(function (key) {
return key
})
// ctCells.push(`<td colspan="2"> </td>`)
// questionCells.push(`<td colspan="${lent+1}"> </td>`)
userCells.push(`<td><div style="display:flex; flex-direction:row">
${
users.forEach(val => {
`<div style="display:flex; flex-direction:column">
<div>${val.id}${val.name}</div>
<div>${val.updatedAt}</div>
<div style="display:flex; flex-direction:column">
${user.data.forEach(IN => {
`
<div style="display:flex; flex-direction:row">
<div><p>${console.log(IN.git_ids)}</p></div>
</div>
`
})}
</div>
</div>`
})
}
</div></td>`)
// userCells.push(`<td id=${user.id}>UserId--- ${user.id} UserName---- ${user.name}${a.map(value => {
// return `
// <div id="${user.id}" >
// <td><input type="checkbox" style="display: inline;"> </td>
// <td> <span id="text">${Object.keys(value)[0]}</span></td>
// <td> <textarea type="text" class="gitplc" placeholder="GitId">${ value.git_ids}</textarea> </td> j
// </div>
// `
// })
// }</td><td>${user.updatedAt}</td>`);
})
});
console.log(userCells)
$.each(ctCells, function (i) {
console.log(userCells)
$tBody.append(`<tr>${ctCells[i]}${questionCells[i]}${userCells[i]}</tr>`)
})
});
});
#scrlSec {
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
}
/* .checkSec { width: 60%; } */
.checkSec .tbl {
padding-top: 20px;
}
.checkSec td {
padding-left: 10px;
}
.checkSec .btnGreen {
color: white;
background-color: #4CAF50;
border: none;
padding: 15px 32px;
width: 100%;
text-decoration: none;
}
.checkSec .gitplc {
width: 80%;
}
.checkSec #text {
font-size: 14px;
}
.checkSec .tbl .colOne {
width: 50%;
float: left;
}
.checkSec .tbl .colTwo {
width: 50%;
float: right;
}
#user {
overflow: auto;
}
.flex-container {
display: flex;
}
th,
td {
font-weight: normal;
padding: 5px;
text-align: center;
width: 120px;
vertical-align: top;
}
th {
background: #00B0F0;
}
tr+tr th,
tbody th {
background: #DAEEF3;
}
tr+tr,
tbody {
text-align: left
}
table,
th,
td {
border: solid 1px;
border-collapse: collapse;
table-layout: fixed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id='userdata'>
<thead>
<tr>
<th colspan="2" id="ct">CT INFO</th>
<th colspan="4" id="que">Question</th>
<th id="user">User Info</th>
</tr>
<tr>
<th>CT ID</th>
<th>CT</th>
<th>Id</th>
<th>isActive</th>
<th>is Complex</th>
<th>is Breakdown</th>
<th>USER</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
I am printing json data in table format in html. But in users column i am getting undefined but when i print those variable in console that is printing correct value.
This is my json api . When i print value in foreach loop of user i am getting undefined but on console.log i have proper value printing in console.
https://api.myjson.com/bins/18g7fm
This is happening because you are using forEach() inside a template literal. forEach() returns undefined by default. Use map() function instead.
userCells.push(`<td><div style="display:flex; flex-direction:row">
${
users.map(val => {
return `<div style="display:flex; flex-direction:column">
<div>${val.id}${val.name}</div>
<div>${val.updatedAt}</div>
<div style="display:flex; flex-direction:column">
${user.data.map(IN => {
return `
<div style="display:flex; flex-direction:row">
<div><p>${console.log(IN.git_ids)}</p></div>
</div>
`
})}
</div>
</div>`
})
}
</div></td>`)
Currently I have a number of clickable boxes that when I hover over them, they change colour. When I click and hold on a specific box, it changes colour.(by using :active in css.
Is there anyway I can make the colour of the border change permanently until a different box is clicked? E.G the same as the :active property except I don't have to keep the mouse held in?
My Code:
flight-viewer.html
<h3>Flights </h3>
<div>
<ul class= "grid grid-pad">
<a *ngFor="let flight of flights" class="col-1-4">
<li class ="module flight" (click)="selectFlight(flight)">
<h4>{{flight.number}}</h4>
</li>
</a>
</ul>
</div>
<div class="box" *ngIf="flightClicked">
<!--<flight-selected [flight]="selectedFlight"></flight-selected>-->
You have selected flight: {{selectedFlight.number}}<br>
From: {{selectedFlight.origin}}<br>
Leaving at: {{selectedFlight.departure || date }}<br>
Going to: {{selectedFlight.destination}}<br>
Arriving at: {{selectedFlight.arrival || date}}
</div>
flight-viewer.css:
h3 {
text-align: center;
margin-bottom: 0;
}
h4 {
position: relative;
}
ul {
width: 1600px;
overflow-x: scroll;
background: #ccc;
white-space: nowrap;
vertical-align: middle;
}
div
{
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
}
li {
display: inline-block;
/* if you need ie7 support */
*display: inline;
zoom: 1
}
.module {
padding: 20px;
text-align: center;
color: #eee;
max-height: 120px;
min-width: 120px;
background-color: #607D8B;
border-radius: 2px;
}
.module:hover {
background-color: #EEE;
cursor: pointer;
color: #607d8b;
}
.module:active {
border: 5px solid #73AD21;
}
.box {
text-align: center;
margin-bottom: 0;
margin: auto;
width: 600px;
position:absolute;
top: 180px;
right: 0;
height: 100px;
border: 5px solid #73AD21;
text-align: center;
display: inline-block;
}
flight-viewer-component.ts:
import { Component } from '#angular/core';
import { FlightService } from './flight.service';
import { Flight } from './flight.model'
#Component({
selector: 'flight-viewer',
templateUrl: 'app/flight-viewer.html',
styleUrls: ['app/flight-viewer.css']
})
export class FlightViewerComponent {
name = 'FlightViewerComponent';
errorMessage = "";
stateValid = true;
flights: Flight[];
selectedFlight: Flight;
flightToUpdate: Flight;
flightClicked = false;
constructor(private service: FlightService) {
this.selectedFlight = null;
this.flightToUpdate = null;
this.fetchFlights();
}
flightSelected(selected: Flight) {
console.log("Setting selected flight to: ", selected.number);
this.selectedFlight = selected;
}
flightUpdating(selected: Flight) {
console.log("Setting updateable flight to: ", selected.number);
this.flightToUpdate = selected;
}
updateFlight(flight: Flight) {
let errorMsg = `Could not update flight ${flight.number}`;
this.service
.updateFlight(flight)
.subscribe(() => this.fetchFlights(),
() => this.raiseError(errorMsg));
}
selectFlight(selected: Flight) {
console.log("Just click on this flight ", selected.number, " for display");
this.flightClicked = true;
this.selectedFlight = selected;
}
private fetchFlights() {
this.selectedFlight = null;
this.flightToUpdate = null;
this.service
.fetchFlights()
.subscribe(flights => this.flights = flights,
() => this.raiseError("No flights found!"));
}
private raiseError(text: string): void {
this.stateValid = false;
this.errorMessage = text;
}
}
Thanks!
I'm quite sure that this has already been answered.
Make your DIVs focusable, by adding tabIndex:
<div tabindex="1">
Section 1
</div>
<div tabindex="2">
Section 2
</div>
<div tabindex="3">
Section 3
</div>
Then you can simple use :focus pseudo-class
div:focus {
background-color:red;
}
Demo: http://jsfiddle.net/mwbbcyja/
You can use the [ngClass] directive provided by angular to solve your problem.
<a *ngFor="let flight of flights" class="col-1-4">
<li [ngClass]="{'permanent-border': flight.id === selectedFlight?.id}" class ="module flight" (click)="selectFlight(flight)">
<h4>{{flight.number}}</h4>
</li>
</a>
This will add the css class permantent-border to the <li> element, if the id of the flight matches the id with the selectedFlight (Assuming you have an id proberty specified, or just use another proberty which is unique for the flight)
I am having some issues with React JS rendering children when rendering the parent.
Here I am trying to implement the "Game of Life" (a project from Freecodecamp class).
I am stuck in this situation. I click on a dead cell and it becomes alive (blue). Then, suppose I want to clear the grid, that is, make all cells dead, but it doesn't work. It seems that even re-rendering the parent will not re-render the children.
Any idea?
var board = [];
var width = 80;
var length = 50;
var cells = width * length;
var states = ["alive", "dead"];
class BoardGrid extends React.Component {
constructor(props) {
super(props);
//this.initBoardArray = this.initBoardArray.bind(this);
}
render() {
//this.initBoardArray();
let boardDOM = this.props.board.map(function(cell) {
return <BoardGridCell status={cell.status} id={cell.id} />;
});
return (
<div id="game-grid">
{boardDOM}
</div>
);
}
}
class BoardGridCell extends React.Component {
render() {
return (
<div
id={this.props.id}
className={`cell ${this.props.status}`}
data-status={this.props.status}
/>
);
}
}
function initBoard() {
for (let cellIndex = 0; cellIndex < cells; cellIndex++) {
let cell = { id: cellIndex, status: "dead" };
board[cellIndex] = cell;
}
}
function drawBoard() {
ReactDOM.render(
<BoardGrid board={board} />,
document.getElementById("game-grid-wrapper")
);
}
function clearBoard() {
for (let cellIndex = 0; cellIndex < cells; cellIndex++) {
let cell = { id: cellIndex, status: "dead" };
board[cellIndex] = cell;
}
}
$("#game-grid-wrapper").on("click", ".cell", function() {
let currentState = $(this).attr("data-status");
let currentStateIndex = states.indexOf(currentState);
let newState = states[(currentStateIndex + 1) % 2];
$(this).attr("class", `cell ${newState}`);
$(this).attr("data-status", newState);
});
$("#stop").on("click", function() {
alert("clearing");
clearBoard();
drawBoard();
});
initBoard();
drawBoard();
html,
body {
height: 100%;
text-align: center;
font-family: 'Open Sans', sans-serif;
}
h1,
h2 {
font-family: 'Press Start 2P', cursive;
}
.button {
width: 100px;
border: 1px solid #555;
padding: 5px;
margin: 5px;
cursor: pointer;
border-radius: 4px;
}
.button:hover {
opacity: 0.9;
}
#main {
margin: 10px;
}
#game-grid {
background-color: #000;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 800px;
height: 500px;
overflow: hidden;
}
#game-grid .cell {
border: 1px solid #767676;
width: 10px;
height: 10px;
color: #fff;
font-size: 9px;
box-sizing: border-box;
}
.alive {
background-color: #2185d0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
<div id="game-actions">
<div id="start" class="button"><i class="fa fa-play"></i> Start</div>
<div id="pause" class="button"><i class="fa fa-pause"></i> Pause</div>
<div id="stop" class="button"><i class="fa fa-stop"></i> Stop</div>
</div>
<div id='game-grid-wrapper'></div>
</div>
You should not use jQuery together with React if you don't have to. Both manipulate the DOM but based on different information which can make them interfere in an unexpected way.
For your board state you should use the state of your BoardGrid component. Initialize your state in the constructor and add an onClick() callback to each cell when rendering it.
When the cell is clicked call the callback function given by the board component and pass it's id with it. Use that id to update the board state using setState() in your BoardGrid component.
I can add some example code later, if you struggle with anything.
UPDATE WITH SOLUTION:
I moved the unordered list to go after the go button,
<div class="row">
<form class="navbar-form suggest-holder">
<input class="form-control input-lg suggest-prompt" type="text" placeholder="Search...">
<select class="form-control input-lg">
<option>All</option>
<option>one</option>
<option>two</option>
<option>three</option>
<option>four</option>
<option>five</option>
<button type="submit" class="btn btn-primary btn-lg">Go</button>
<ul></ul>
</form>
</div>
and added the following to my CSS:
.form-control {
float: left;
}
I have a search bar in my header that is using a JS function for autocomplete based on a small list of sample products.
My site has several categories that can be searched, similar to Amazon, so to the right of the search input box, there is a drop-down menu and a "go" button.
Before I added the autocomplete JS, these were horizontally aligned. Now, the category drop-down and the go button are being pushed below the search input box.
How can I get them horizontally aligned again? I have included a fiddle with the HTML/CSS
HTML
<div class="col-xs-12 col-sm-9 col-md-9 col-lg-9 search-block">
<div class="row">
<form class="navbar-form suggest-holder">
<input class="form-control input-lg suggest-prompt" type="text" placeholder="Search...">
<ul></ul>
<select class="form-control input-lg">
<option>All</option>
<option>one</option>
<option>two</option>
<option>three</option>
<option>four</option>
<option>five</option>
</select>
<button type="submit" class="btn btn-primary btn-lg">Go</button>
</form>
</div>
</div>
CSS:
.suggest-holder {
input {
border: 1px solid $gray-lighter;
}
ul {
list-style: none;
border: 1px solid $gray-lighter;
background-color: white;
width:65%;
}
li {
padding: 5px;
position: inherit;
}
li:hover {
cursor: pointer;
}
li:hover, li.active {
background: rgba(100,100,100,.2);
}
}
.suggest-name {
font-weight: bold;
display: block;
margin-left: 40px;
}
.suggest-sku {
font-style: italic;
font-size:$font-size-small;
color: $gray-light;
}
.suggest-image {
height: 35px;
float: left;
padding-right: 5px;
margin-top: -20px;
}
header .search-block {
input {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
width: 65% !important;
#media (max-width:$screen-xs) {
width: 47% !important;
}
}
select {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
padding-top: 5px;
padding-left: 5px;
padding-bottom: 5px;
padding-right: 5px;
margin-left: -5px;
width: 20% !important;
#media (max-width:$screen-xs) {
width: 30% !important;
}
}
button {
vertical-align: top;
width: 14% !important;
#media (min-width:$screen-lg) {
width: 8% !important;
}
#media (max-width:$screen-xs) {
width: 21% !important;
}
}
.form-group {
> form {
> * {
display: inline-block;
}
#media (max-width:$screen-xs) {
text-align: center;
margin-left: 0;
margin-right: 0;
#include pad-sides(0);
}
}
}
}
input:focus::-webkit-input-placeholder { color:transparent; }
input:focus:-moz-placeholder { color:transparent; } /* FF 4-18 */
input:focus::-moz-placeholder { color:transparent; } /* FF 19+ */
input:focus:-ms-input-placeholder { color:transparent; } /* IE 10+ */
JS
var data = [
{
image: src = "http://placehold.it/35x35",
name: 'apples',
sku: '61583318'
},
{
image: src = "http://placehold.it/35x35",
name: 'oranges',
sku: '924335'
},
{
image: src = "http://placehold.it/35x35",
name: 'grapes',
sku: '73940'
},
{
image: src = "http://placehold.it/35x35",
name: 'strawberries',
sku: '66155'
},
{
image: src = "http://placehold.it/35x35",
name: 'string beans',
sku: '112509'
},
{
image: src = "http://placehold.it/35x35",
name: 'apricot',
sku: '112984'
}
];
// Suggest section holder
var $suggestedHL = $('.suggest-holder');
// Suggestions UL
var $suggestedUL = $('ul', $suggestedHL);
// Suggestions LI
var $suggestedLI = $('li', $suggestedHL);
// Selected Items UL
var $selectedUL = $('#selected-suggestions');
// Keyboard Nav Index
var index = -1;
function addSuggestion(el){
$selectedUL.append($('<li>' + el.find('.suggest-name').html() + '</li>'));
}
$('input', $suggestedHL).on({
keyup: function(e){
var m = false;
if(e.which == 38){
if(--index < 0){
index = 0;
}
m = true;
}else if(e.which == 40){
if(++index > $suggestedLI.length - 1){
index = $suggestedLI.length-1;
}
m = true;
}
if(m){
// Remove the active class
$('li.active', $suggestedHL).removeClass('active');
$suggestedLI.eq(index).addClass('active');
}else if(e.which == 27){
index = -1;
// Esc key
$suggestedUL.hide();
}else if(e.which == 13){
// Enter key
if(index > -1){
addSuggestion($('li.active', $suggestedHL));
index = -1;
$('li.active', $suggestedHL).removeClass('active');
}
}else{
index = -1;
// Clear the ul
$suggestedUL.empty();
// Cache the search term
$search = $(this).val();
// Search regular expression
$search = new RegExp($search.replace(/[^0-9a-z_]/i), 'i');
// Loop through the array
for(var i in data){
if(data[i].name.match($search)){
$suggestedUL.append($("<li><span class='suggest-name'>" + data[i].name + "</span><span class='suggest-sku'>" + data[i].sku + "</span><img src=" + data[i].image + " class='suggest-image'/></li>"));
}
}
// Show the ul
$suggestedUL.show();
}
if($(this).val() == ''){
$suggestedUL.hide();
}
},
keydown: function(e){
if(e.which == 38 || e.which == 40 || e.which == 13){
e.preventDefault();
}
},
focus: function(e){
if($(this).val() != ''){
$suggestedUL.show();
}
}
});
$suggestedHL.on('click', 'li', function(e){
addSuggestion($(this));
});
$('body').on('click', function(e) {
if (!$(e.target).closest('.suggest-holder li, .suggest-holder input').length) {
$suggestedUL.hide();
};
});
https://jsfiddle.net/sox0sxmz/1/
Try putting display: inline-block on all of the elements. E.g. form > * { display: inline-block;}. JSfiddle
You could also use float: left to not have the additional margins JSfiddle.
UPDATE:
Use:
.form-control {
float: left;
}
JSfiddle
There is a unordered list just before the select box which is causing the problem. It's defined as display:block and forcing the select box and the button to the next line.
You need another wrapper for the input and ul element which shows the results. Then you need elements with display-inline.
Here your jsfiddle: https://jsfiddle.net/sox0sxmz/8/
<div id-"my-container">
<input class="form-control input-lg suggest-prompt" type="text" placeholder="Search...">
<ul></ul>
</div>
Then apply a float:left to the container.
CSS:
form > * {
display: inline-block;
}
#my-container{
float: left;
}