I have a provider that provides tags for news articles (list with news). If they are more than three, then the additional tags (>3) will be grouped together (called plusTags in the example code). I can see in my console that I have all the tags, but they are not distributed correctly. Ex.
On the first page I have four news with the distributed tags "a,b,c", b", "ac" "b". On the next page, the news articles are (obviously) different, but the distribution of the tags is the SAME ("a,b,c", b", "ac" "b") as on the first page, even if the tags should be different. So the distribution follows the same pattern. I suspect it's my code in my "componentDidMount" function, as its there where I distribute all the tags. Suspect it might repeat because this function repeats itself?
public componentDidMount(): void {
let tags = [];
let plusTags = [];
if (this.props.tags != null) {
if (this.props.tags.length > 0) {
for (var i = 0; i < 3; i++) {
if (this.props.tags[i] != undefined) {
tags.push(this.props.tags[i] + " ");
}
}
for (var j = this.props.tags.length - 1; j >= 3; j--) {
if (this.props.tags[i] != undefined) {
plusTags.push(this.props.tags[j] + " ");
}
}
} else {
tags = this.props.tags;
}
}
this.setState({
tags: tags,
plusTags: plusTags
});
}
and in my render
public render(): React.ReactElement<INewsTagsProps> {
return <React.Fragment>
<div className={styles.tagContainer}>
{
this.state.tags ?
this.state.tags.map((t) => {
if (this.props.background == BackgroundType.None) {
return (
<a href={this.props.tagPageUrl + "?tag="+ t}>
<span className={styles.tagNewsTiles}>{t}</span>
</a>);
}
else {
return(
<a href={this.props.tagPageUrl + "?tag="+ t}>
<span className={styles.tagFollowedNews}>{t}</span>
</a>);
}
})
: null
}
{this.state.plusTags.length > 0 ?
<span className={`callout-target-${this.state.targetIndex} ${this.props.background == BackgroundType.None ? styles.tagNewsTiles : styles.tagFollowedNews}`}
onClick={(e) => {e.stopPropagation(); this.setState({plusTagsDialogOpen: true});}}>+ {this.state.plusTags.length}</span>
:
null
}
</div>
<Callout
className="ms-CalloutExample-callout"
gapSpace={0}
target={this.state.target}
onDismiss={() => this.closeDialog()}
hidden={!this.state.plusTagsDialogOpen}
isBeakVisible={true}
beakWidth={10}
directionalHint={DirectionalHint.topCenter}
>
<div className={styles.tagPopupWrapper}>
<div className={styles.tagPopupContainer}>
{this.state.plusTags ?
this.state.plusTags.map((t) => {
if (this.props.background == BackgroundType.None) {
return (
<a href={this.props.tagPageUrl+ "?tag="+t}>
<span className={styles.tagNewsTiles}>{t}</span>
</a>);
}
else {
return(
<a href={this.props.tagPageUrl+ "?tag="+t}>
<span className={styles.tagFollowedNews}>{t}</span>
</a>);
}
}):
null}
</div>
</div>
</Callout>
;
Related
I was sitting with my problem for few hours already trying different solutions but nothing seems to work properly. I am displaying cards on my webpage fetched from database. Under each card I want to display Book a ticket button based on if ticket is available (if particular concert have a ticket and if the ticket is not used).
I have two tables: concerts and tickets There is over 2000 concerts but only 68 tickets for around 20 - 30 concerts. So most of concerts don't have a ticket at all, and some of concerts have multiple tickets, and some concerts have one ticket.
What I tried to do was to loop trough concerts then nest loop for tickets inside and get concerts which has a ticket but then I realized I also need to check if ticket is used to be able to properly display a button. It just getting too messy and way too complex in order to display a regular button.
Is there some way around it? Do I need to change my data base structure? What I actually need to do?
So once again: I need to display a button Book a ticket if particular concert has a ticket/tickets and if that ticket/tickets are unused (at least one unused), otherwise button should be gray with another text on it. Any suggestions?
Ticket table:
Concert table
And here is how my page look like:
UPDATE:
I managed to make a function to get all concerts with non booked tickets:
let concertsWithTickets = [];
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
concertsWithTickets.push(filteredData[j]);
}
}
}
Then i try to loop again inside the view but i got syntax error.
<div>
{for(let i = 0; concerts.length < i; i++)
{
if(concertsWithTickets.id == concert.id) ? <BookBtn/> :
<BookBtn color="gray"/>}
}
</div>
Here is the whole code without import stuff
useEffect(() => {
let concertsWithTickets = [];
const loadConcerts = async () => {
const resConcerts = await axios.get("/data/concerts");
const resTickets = await axios.get("/data/tickets");
// getting all concerts above today
const filteredData = resConcerts.data.filter((concert) => {
return concert.datum >= currentDate;
});
for (let i = 0; i < resTickets.data.length; i++) {
for (let j = 0; j < filteredData.length; j++) {
if (
resTickets.data[i].concertid == filteredData[j].id &&
resTickets.data[i].booked == 0
) {
concertsWithTickets.push(filteredData[j]);
}
}
}
setConcerts(
filteredData.sort((a, b) =>
a.datum > b.datum ? 1 : a.datum < b.datum ? -1 : 0
)
);
};
loadConcerts();
console.log(concertsWithTickets);
}, []);
if (!concerts.length) {
return <p className="center">Loading...</p>;
}
return (
<div className="center">
<h1 className="textpink">Upcoming concerts </h1>
<hr className="stylez" />
{concerts.slice(0, limit ? limit : concerts.length).map((concert)
=> {
return (
<div className="cards-container " key={concert.id}>
<div className="card">
<h3>
{concert.name.length > 32
? concert.name.slice(0, 32) + "..."
: concert.name}
</h3>
<h2>{concert.id}</h2>
<img
src={concert?.image ? concert.image : defaultpicture}
alt="Band-Image"
/>
<div>
<p className="label">{concert.datum}</p>
<p className="cardAdress">
{concert.venue.length > 30
? concert.venue.slice(0, 27) + "..."
: concert.venue}
</p>
</div>
<div>
{for(let i = 0; concerts.length < i; i++)
{
if(concertsWithTickets.id == concert.id) ? <BookBtn /> : <BookBtn color="gray"/>}
}
</div>
</div>
</div>
);
})}
<div>
<hr className="stylez" />
<button className="btn-singup " onClick={() => setLimit(limit + 5)}>
Show more
</button>
</div>
</div>
);
};
I'm busy developing a wordpress plugin to look for numbers and hide them by formating the number and replacing it with 0000..., Example:
<a href="tel:0000000000">
<span>
<span>0000 000 000</span>
</span>
</a>
I have javascript that queries the <a href=""> tag. I then get the children of the a tag. However, my issue is that because I don't know what or how many children ill be working with i can't assume it will be 1 or 2 thus I have to predict and look for it.
Javascript code:
// REMOVE SPACES IN STRING
let replaceStr = function (self) {
let value = self.replace(/[- )(]/g, '')
return value
};
// REMOVE LETTERS FROM STRING
let rmLetters = function (self) {
// let value = self.replace( /^\D+/g, '')
let value = self.replace(/\D+%?/g, "");
return value
}
let a = document.querySelectorAll("a[href^='tel:'], a[href^='Tel:'], a[href^='callto:']");
for (var i = 0; i < a.length; i++) {
let hrefSlice = a[i].href.slice(4);
let countChildren = a[i].childElementCount
if (a[i].hasChildNodes()) {
let a_childNodes = a[i].children;
if (a_childNodes.length > 1) {
for (let l = 0; l < a_childNodes.length; l++) {
if (replaceStr(a_childNodes[l].textContent) === hrefSlice) {
a_childNodes[l].textContent = replaceStr(a_childNodes[l].textContent).slice(0, 4) +
"...Click Here";
} else if (replaceStr(rmLetters(a_childNodes[l].textContent)) === hrefSlice) {
a_childNodes[l].textContent = replaceStr(rmLetters(a_childNodes[l].textContent)).slice(
0, 4) + "...Click Here";
}
}
}
}
}
}
Not sure if I got you right but I'd do it like this:
document.querySelector('#hideButton').addEventListener('click', () => {
const phoneAnchors = document.querySelectorAll('a[href^="tel:"], a[href^="Tel:"], a[href^="callto:"]');
phoneAnchors.forEach((phoneAnchor) => {
const phoneNumber = phoneAnchor.href.split(':')[1] || '';
phoneAnchor.href = phoneAnchor.href.replace(/[0-9]/g, '0');
phoneAnchor.querySelectorAll('*').forEach(childNode => {
if (childNode.textContent.replace(/[ ]/g, '') === phoneNumber) {
childNode.textContent = childNode.textContent.replace(/[0-9]/g, '0');
}
});
});
});
a {
display: block;
}
<a href="tel:1234567890">
<span>
<span>1234 567 890</span>
</span>
</a>
<a href="tel:0987654321">
<span>
<span>0987 654 321</span>
</span>
</a>
<a href="tel:1122334455">
<span>
<span>1122334455</span>
</span>
</a>
<hr>
<button id="hideButton">Hide Phone Numbers</button>
I have this help dialog and some reason my information texts are not showing as soon as the dialog is open. It only start to show when I click the subsection so just wonder how I can display it as soon as the user open the dialog box. Any suggestion or help will be really appreciated.
HTML
//Clicking this will take the user each subsection
<div *ngFor="let subSection of mappedSecSub | async; let last=last">
<div *ngIf="subSection.parentId == section.id;" (click)="clicked(subSection.id, section.id)">
<a mat-list-item>{{subSection.name}}
<mat-divider *ngIf="!last" class="solid"></mat-divider>
</a>
</div>
</div>
// This display the informations text
<div class="column right">
<div *ngFor="let section of targetSectionGroup">
<h1 *ngIf="section.parentId == null" class="hint">{{section?.name}}</h1>
</div>
<div *ngFor="let section of targetSectionGroup">
<div *ngIf="section.parentId != null">
<h2 id="s{{section.id}}ss{{section.parentId}}" class="hint">{{section?.name}}</h2>
<p class="multi_lines_text" [innerHTML]="section?.text"></p>
</div>
</div>
</div>
TS
mappedSections: BehaviorSubject<any[]> = new BehaviorSubject<any[]>([]);
mappedSecSub = this.mappedSections.asObservable()
targetSection: { id, name, parentId, text };
targetSectionGroup: { id, name, parentId, text }[] = [];
ngOnInit(): void {
this.fetchData();
}
fetchData = () => {
this.HelpService.getHelp().subscribe(res => {
this.mappedSections.next(res['res'])
let newMappedSection = this.mappedSections.getValue()
for (let i = 0; i < newMappedSection.length; i++) {
const element = newMappedSection[i];
if (element.parentId) {
this.targetSection = element;
break
}
}
})
}
clicked(id, parentId) {
this.targetSectionGroup = [];
let data = this.mappedSections.getValue()
for (let i = 0; i < data.length; i++) {
if (data[i].parentId == parentId || data[i].id == parentId) {
this.targetSectionGroup.push(data[i]);
}
if (data[i].id === id) {
this.targetSection = data[i]
}
}
document.querySelector(`#s${id}ss${parentId}`).scrollIntoView({ behavior: 'smooth' })
}
Right now the information text only show up when I click the subsections. I want to show it as soon as the help dialog is open.
I guess this should work
fetchData = () => {
this.HelpService.getHelp().subscribe(res => {
this.mappedSections.next(res['res']);
const response = res['res'];
this.clicked(response.id, response.parentId);
let newMappedSection = this.mappedSections.getValue()
for (let i = 0; i < newMappedSection.length; i++) {
const element = newMappedSection[i];
if (element.parentId) {
this.targetSection = element;
break
}
}
})
}
I'm making a movie sorter list, you enter the title and then the rating and it will show you the movies in order by rating. I have an array of objects and I managed to sort the array by rating, but I can't find a way to actually display the array in order on the HTML DOM.
I've tried for loops and forEach's but they don't work the way I want.
const movieTitle = document.querySelector(".movie-title");
const movieRating = document.querySelector(".movie-rating");
const movieList = document.querySelector(".movie-list");
const sortBtn = document.querySelector(".btn");
let movieStorage = [];
function sendMovie() {
if(event.keyCode == 13) {
if(movieTitle.value != "" && movieRating.value != "") {
title = movieTitle.value;
rating = parseInt(movieRating.value);
movieStorage.push({
title: title,
rating: rating
});
// If rating of a is bigger than rating of b return 1, if not return -1
movieStorage.sort((a, b) => (a.rating > b.rating) ? -1 : 1);
console.log(movieStorage);
addMovieToList(title, rating);
movieTitle.value = "";
movieRating.value = "";
} else {
console.log("Fields missing");
}
}
}
function addMovieToList(title, rating) {
const div = document.createElement("div");
div.className = "list-items";
div.innerHTML = `
<div class="item-title">
<p>${title}</p>
</div>
<div class="item-rating">
<p>${rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
movieList.appendChild(div);
}
function sortByRating(element) {
for(let i = 0; i < movieStorage.length; i++) {
element.innerHTML = `
<div class="item-title">
<p>${movieStorage[i].title}</p>
</div>
<div class="item-rating">
<p>${movieStorage[i].rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
}
}
document.addEventListener("click", (e) => {
const deleteIcon = e.target;
const item = document.querySelector(".list-items");
if(deleteIcon.classList.contains("delete")) {
deleteIcon.parentElement.parentElement.remove(item);
}
})
tldr demo
After sorting the array, you need a way to reference movie divs to sort them. There are many ways to do it, what I chose is using id. When you create movie <div>, give it an ID unique for each movie name:
// Simple function to generate hash number for each string
function hashStr(stringValue) {
var hash = 0, i, chr;
if (stringValue.length === 0) return hash;
for (i = 0; i < stringValue.length; i++) {
chr = stringValue.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
const MOVIES = [
{name: "a", rating: 3},
{name: "b", rating: 6},
{name: "c", rating: 3},
{name: "d", rating: 2},
{name: "e", rating: 1},
];
function showMovies() {
const moviesDiv = document.querySelector("#movies");
for(const movie of MOVIES)
{
const id = "movie-"+hashStr(movie.name);
// If there's no element with the ID, we need to create the DIV for the movie
if(!document.querySelector("#"+id)) {
const elm = document.createElement("div");
elm.appendChild(new Text(movie.name + " ("+movie.rating+"/10)"));
elm.id = id;
elm.classList.add("movie");
moviesDiv.appendChild(elm);
}
}
}
Then, when sorting, you can reference each movie by ID:
// Sort movies using given property (eg. "name")
// The second param determines sort direction
function sortBy(property, ascending=true) {
MOVIES.sort((a,b) =>{
return cmp(a[property], b[property], ascending);
});
// Now after sorting the array, we can sort the HTML elements
const moviesDiv = document.querySelector("#movies");
let lastMovie = null;
for(const movie of MOVIES)
{
const id = "#movie-"+hashStr(movie.name);
const movieDiv = document.querySelector(id);
console.log(id, movieDiv);
// If created
if(movieDiv) {
// remove and append after last processed movie (for the first movie, this will append to top)
moviesDiv.insertBefore(movieDiv, lastMovie);
}
}
}
// Compare string and number, makes no sense for other types
function cmp(a,b, ascending=true) {
if(typeof a=='number' && typeof b == "number") {
return ascending ? a-b : b-a;
}
else if(typeof a=='string' && typeof b == "string"){
return (ascending ? 1 : -1) * a.localeCompare(b);
}
else {
return 0;
}
}
When you add a movie, you just call sort again. You will need to remember the last sorting parameters for that.
Your sort will work fine. The problem is that after you've sorted you can't just display that movie, you have to redisplay the entire list. You're almost there with your sortByRating method, but it doesn't recreate the entire list correctly. Try something like:
function showMoviesList(element) {
let innerHTML = "";
for (let i = 0; i < movieStorage.length; i++) {
innerHTML += `
<div class="item-title">
<p>${movieStorage[i].title}</p>
</div>
<div class="item-rating">
<p>${movieStorage[i].rating}</p>
</div>
<div class="item-delete">
<i class="fa fa-trash trash-icon delete"></i>
</div>
`;
}
element.innerHTML = innerHTML;
}
This resets the inner HTML of the element to the complete movie list in order every time it's called.
Now call showMoviesList(movieList) instead of calling addMovieToList in sendMovie.
i have this Panel array coming from backend which has another array Tests. i have mapped them on my custom accordion with checkboxes. the problem i am facing is i should be able to select/deselect Tests without removing it from the from front-end like it Disappears when i deselect. how can i solve this issue?
you can see from that image
https://i.stack.imgur.com/qJUFy.png
here is my html file
<ngb-panel *ngFor="let panel of panels" id="{{panel.Id}}" [title]="panel.Name">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" [name]="panel.Id + '-' + panel.Moniker" [ngModel]="checkAllTestsSelected(panel)" (ngModelChange)="onPanelCheckboxUpdate($event, panel)" [id]="panel.Id + '-' + panel.Moniker">
<span class="custom-control-indicator"></span>
</label>
</ng-template>
<ng-template ngbPanelContent>
<div class="individual-panel" *ngFor="let test of panel.Tests">
<span class="text-dimmed">{{test.Name}}</span>
<span *ngIf="panel.Name.includes('ENA') || panel.Name.includes('Celiac')">
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" [name]="test.Id + '-' + test.Code"
[ngModel]="testSelectionSession.SelectedPanelIds.indexOf(panel.Id) > -1 || testSelectionSession.SelectedPanelIds.indexOf(test.AssociatedPanel?.Id) > -1"
(ngModelChange)="onTestCheckboxUpdate($event, test, panel)"
[id]="test.Id + '-' + test.Code">
<span class="custom-control-indicator"></span>
</label>
</span>
</div>
ts file
checkAllTestsSelected(panel: TestOrderPanel) {
// get all individual test panels
let individualTestPanelIds = panel.Tests.reduce((acc, test) => {
if (test.AssociatedPanel) {
acc.push(test.AssociatedPanel.Id);
}
return acc;
}, []);
// check if all individual test panels are selected
let allIndividualTestsSelected = individualTestPanelIds.reduce(
(acc: boolean, panelId: number) =>
acc && this.panelIds.indexOf(panelId) > -1,
individualTestPanelIds.length > 0 &&
panel.Tests.length === individualTestPanelIds.length
);
// if selected, remove all individual test panels and add the panel group
if (panel.Tests.length > 0 && allIndividualTestsSelected) {
this.panelIds = this.panelIds.filter(
panelId => individualTestPanelIds.indexOf(panelId) === -1
);
this.selectedPanels = this.selectedPanels.filter(
selectedPanel => individualTestPanelIds.indexOf(selectedPanel.Id) === -1
);
this.panelIds.push(panel.Id);
this.selectedPanels.push(panel);
this.updateSession();
}
return this.panelIds.indexOf(panel.Id) > -1;
}
onPanelCheckboxUpdate($event: boolean, panel: TestOrderPanel) {
let testPanelIds = panel.Tests.reduce((acc, test) => {
if (test.AssociatedPanel) {
acc.push(test.AssociatedPanel.Id);
}
return acc;
}, []);
// Wipe any duplicates
this.panelIds = this.panelIds.filter(
panelId => panel.Id !== panelId && testPanelIds.indexOf(panelId) === -1
);
this.selectedPanels = this.selectedPanels.filter(
selectedPanel =>
panel.Id !== selectedPanel.Id &&
testPanelIds.indexOf(selectedPanel.Id) === -1
);
if ($event) {
this.panelIds.push(panel.Id);
this.selectedPanels.push(panel);
}
this.updateSession();
}
onTestCheckboxUpdate($event: boolean,
test: TestOrderPanelTest,
panel: TestOrderPanel,
index) {
let testPanelIds = panel.Tests.reduce((acc, test) => {
if (test.AssociatedPanel) {
acc.push(test.AssociatedPanel.Id);
}
return acc;
}, []);
let associatedTestPanels =
this.testSelectionSession.IndividualTestPanelsForAll.filter(
testPanel => testPanelIds.indexOf(testPanel.Id) > -1
);
// If the panel is selected and a test within the panel is deselected,
// remove the panel and back all of the individual tests
if (this.panelIds.indexOf(panel.Id) > -1 && !$event) {
this.selectedPanels = this.selectedPanels.filter(
e => e.Tests.splice(index, 1)
);
}
let clickedTestPanel = associatedTestPanels.find(
testPanel => (test.AssociatedPanel ? test.AssociatedPanel.Id : -1) ===
testPanel.Id
);
if (clickedTestPanel) {
// Wipe any duplicates
this.panelIds = this.panelIds.filter(
panelId => clickedTestPanel.Id !== panelId
);
this.selectedPanels = this.selectedPanels.filter(
panel => clickedTestPanel.Id !== panel.Id
);
// Add individual panel if checkbox selected
if ($event) {
this.panelIds = this.panelIds.concat(clickedTestPanel.Id);
this.selectedPanels = this.selectedPanels.concat(clickedTestPanel);
}
}
this.updateSession();
}
this.panelIds includes IDs of panels and this.selectedPanels includes whole panel array which is selected.
i have created a stackblitz too
my code is doing something like that stackblitz.com/edit/angular-bsszc9
and here is example of how my page will look like
stackblitz
how can i solve this problem?
thanks
Delete
if (this.panelIds.indexOf(panel.Id) > -1 && !$event) {
this.selectedPanels = this.selectedPanels.filter(
e => e.Tests.splice(index, 1)
);
}
This part doesn't do anything but removing checkboxes and panels for no logical reason in a very random way (removing the checkbox with the same index from each panel and if there isn't any remove the whole panel)
Please read: Array.prototype.splice and Array.prototype.filter to understand how is this happening.