Filter search results react - html

My data back from search result has columns: enTitle,Image,url,enDescription,HasLandingPage,AddInfo.
I want to filter search results by AddInfo to show in different lists. later if I can add a button that would be better.
Render Data:
const ListArticle = (props) =>{
return (
<div className="card">
<div className="search-img-lft">
<a href={props.link} target="_blank">
<img src={props.image} alt="" />
</a>
</div>
<div className="search-imgcont-rgt">
<a href={props.link} target="_blank">
<h3>
{props.title}
{props.kind} // just to see if kind works
</h3>
<p>{props.desc}</p>
</a>
{props.link}
</div>
</div>
);
}
List Class:(ignore the i,brac & lim they are for pagination)
class List extends React.Component {
render(){
const liArt =[];
const searchText = this.props.searchText.toLowerCase().replace(/[^a-z0-9]/g, '');
var i = 0;
const brac = this.props.start;
const lim = brac + this.props.qtyPerPage;
//the filter below works for resources but I want all to be filtered and show in the list in previous code snippet
this.props.list.filter(u=>u.AddInfo == "resource").map((article)=>{
var artText = (article.enTitle + " " + article.URL + " " + article.enDescription + " " + article.AddInfo).toLowerCase().replace(/[^a-z0-9]/g, '');
if(artText.indexOf(searchText)===-1){
return;
}
i++;
if(brac<i && i<lim){
liArt.push(
<ListArticle key={article.Image+article.URL}
title={article.enTitle}
image={article.Image+"?h=100&mode=crop&scale=down"}
link={JSON.stringify(article.HasLandingPage).toUpperCase()=="TRUE" ? "/en/"+article.URL : "/" + article.URL}
desc={article.enDescription}
kind={article.AddInfo.includes("SKU") ? " Product" : (article.AddInfo.includes("resource") ? " Resource" : " Page")} />
);//push
} //limit check
});//map
return (
<div className="search-page-listbox">
{liArt}
</div>
);
}
}

If i got you right, you want to create multiple lists while each list shows items of another"AddInfo".
First, I would recommend to separate your task into three components (instead of two):
First component is the ListArticle which will be the list item,
Second will be the component List -> that will receive the list you want to show (after they have been filtered),
Last component will be ListContainer -> this one will hold multiple lists (as many as the options of AddInfo).
Then, in ListContainer you can go over all unique AddInfo, and create List component for every option - passing only filtered items:
ListArticle.js
import React from 'react';
const ListArticle = (props) =>{
return (
<div className="card">
<div className="search-img-lft">
<a href={props.link} target="_blank">
<img src={props.image} alt="" />
</a>
</div>
<div className="search-imgcont-rgt">
<a href={props.link} target="_blank">
<h3>
{props.title}
{props.kind} // just to see if kind works
</h3>
<p>{props.desc}</p>
</a>
{props.link}
</div>
</div>
);
}
export default ListArticle;
List.js
import React from 'react';
import ListArticle from './ListArticle';
export default class List extends React.Component {
render() {
return (
this.props.list.map(article => <ListArticle key={article.Image + article.URL}
title={article.enTitle}
image={article.Image + "?h=100&mode=crop&scale=down"}
link={JSON.stringify(article.HasLandingPage).toUpperCase() == "TRUE" ? "/en/" + article.URL : "/" + article.URL}
desc={article.enDescription}
kind={article.AddInfo.includes("SKU") ? " Product" : (article.AddInfo.includes("resource") ? " Resource" : " Page")} />
)
)
}
}
ListContainer.js
import React from 'react';
import List from './List';
class ListContainer extends React.Component {
constructor(props){
super(props);
}
render() {
let lists = {};
let searchText = this.props.searchText;
if(this.props){
let filteredList = this.props.list.filter(article=>(article.enTitle + " " + article.URL + " " + article.enDescription + " " + article.AddInfo).toLowerCase().replace(/[^a-z0-9]/g, '').indexOf(searchText)!==-1);
filteredList && filteredList.forEach(u => {
if(lists[u.AddInfo]===undefined) lists[u.AddInfo]=[];
lists[u.AddInfo].push(u);
});
}
return(
Object.keys(lists).map(function(key, index) {
return <List list={lists[key]} />
})
)
}
}
export default ListContainer;
Usage:
<ListContainer list={list} searchText={searchText} />
Hope it helped :)

I'd return something like this from your List class (I tried explaining my thought process in comments inside the code):
return (<React.Fragment>
{
// second (kinda), I'd convert the inside generated collection (object) into an array
// -> where the array elements are now ["AddInfo type", [array of elements with that type]]
Object.entries(
// first, convert the list into an object, collecting each type of "AddInfo" into
// -> a unique property, and storing all objects w/ that type in an array
this.props.list.reduce((output, u) => {
if (!output[u.AddInfo]) output[u.AddInfo] = [];
output[u.AddInfo].push(u);
return output
}, {})
)
// third, I'd map the new array of ["AddInfo type", [array of elements with that type]] into
// -> the jsx output you want, like this:
.map(([type, array]) => {
return <div className="search-page-listbox">
{array.map((article, i) => {
// ... everything inside your ".map((article...)" function can go here
})}
</div>
})
}
</React.Fragment>)
A few notes:
You can replace var i = 0 and i++ lines with the i index that automatically comes from the second parameter in the map function (see my version of array.map((article, i) => ...)
If you haven't seen things like array destructuring (ex: .map(([type, array]) => ...)) let me know, I can explain. It's a pretty shnazzy thing you can do to save some lines.
My first step was to figure out how to create an object container which holds sorted data based on AddInfo - that's why my // first comment comes technically after the // second comment. Hope that makes sense.
Let me know if you have questions or if there was a typeo that's breaking my code. I haven't tested it obviously since I don't have your react code / variables.

Related

html strings array convert to JSX

`import React from 'react'
export default function Quiz(props){
// generate random index without duplicates
function generateRandomIndex(){
const randomNumArr=[]
for (var a = [0, 1, 2, 3], i = a.length; i--; ) {
var random = a.splice(Math.floor(Math.random() * (i + 1)), 1)[0];
randomNumArr.push(random)
}
return randomNumArr
}
let randomNumbers = generateRandomIndex()
let spreadOptions = ()=>{
let optionsHtmlArray = []
for(let i=0; i<props.answers.length; i++){
optionsHtmlArray.push(`<span className='answers' key=${i} style={${{backgroundColor: props.correct===props.answers[i] ? "green" : "red"}}}>
{ ${props.answers[i]} } </span>`)
}
return optionsHtmlArray
}
return (
<div className='Quiz'>
<h3 className='question'>{props.question}</h3>
<div className='answers_div'>
{ spreadOptions()[randomNumbers[0]] }
{ spreadOptions()[randomNumbers[1]] }
{ spreadOptions()[randomNumbers[2]] }
{ spreadOptions()[randomNumbers[3]] }
</div>
<hr className='hr'/>
</div>)
}
'
'//this is from App.js
// fetch to API when first render to save data to the state,
// and fetch depending on the sate of showOverlay
React.useEffect(() => {
fetch("https://opentdb.com/api.php?amount=5&category=9&difficulty=easy&type=multiple")
.then(res => res.json())
.then(data => {
setQuestions(data.results)
//after set questions state that comes from fetch request
//and set the custom questions with some properties I need
setCustomQuestions(prevQuestions=>{
let newArr=[]
for(let i=0; i<data.results.length; i++){
newArr.push({question: data.results[i].question,
questionId: nanoId(),
answers: [data.results[i].correct_answer].concat(data.results[i].incorrect_answers),
correct: data.results[i].correct_answer})
}
return newArr
})
})
}, [])
// Quiz component properties
const customQuestionsArr = customQuestions.map(question => {
return < Quiz
key={question.questionId}
question={question.question}
answers={question.answers}
correct={question.correct}
/>
})'
Hi all, I am trying to render all options of the answers in Quiz component, however,
spreadOptions() returns an array of html strings for the answers
I gotta parse to JSX to make it work.
I tried to install react-html-parser, didn't work it only gave me a bunch of error every time when I try to install dependencies through npm
I tried dangerouslySetInnerHTML, but also didn't work
Would you be able to provide the props that you are trying to pass to Quiz component?
Below is a snippet of code with modified spreadOptions and jsx. I wasn't able to test this code tho but will update it if you can provide the sample props.
let spreadOptions = props.answers.map((a, i) => (
<span
key={i}
className='answers'
style={{
backgroundColor: props.correct === a ? 'green' : 'red',
}}
>
{a}
</span>
));
return (
<div className="Quiz">
<h3 className="question">{props.question}</h3>
<div className="answers_div">
{spreadOptions}
</div>
<hr className="hr" />
</div>
);

Highlight part of Text

I was building project similar to Google Search using React.js and GoogleAPI.
Here I have got a problem.
For example, if you search "tea" on Google, you can see "Searches related to tea" at the bottom of the result, just above the pagination.
There, the words expect "tea" is bold.
How can I implement it using React?
I know the word "tea" and full text of related searches, but how can I highlight the part of text?
Many help.
Here is my code.
In the code, item.query is the full text such as "black tea", and searchValue is "tea". I just want to make "black" bold.
import React, { Component } from ‘react’;
import { connect } from ‘react-redux’;
class RelatedSearch extends Component {
render() {
var res = this.props.searchResults.related_searches;
return (
<div>
<br/><br/>
<p>Related Search</p>
{res.map((item, index) =>
<Item item={item} key={index} />
)}
</div>
);
}
}
function Item (item, key) {
return <div>{item.item.query}</div>;
}
const mapStateToProps = state => ({
searchResults: state.usersReducer.searchResults,
searchValue : state.usersReducer.searchValue,
});
export default connect(mapStateToProps)(RelatedSearch);
I have got answers here and already accepted it, but I received suggestion not to use dangerous html. Is there any other solution?
Here is what I came up with (it may not work in all edge cases but I tested it and it works in most situations)
First get a handle on the search term
let searchTerm = "Hello";
Then create a function that loops through the current string
createHighlight(text) {
// split the string at the point(s) where the search term is present.
let split = text.toLowerCase().split(searchTerm.toLowerCase());
// create a placeholder string.
let ttt = "";
// loop through the splited string and put in the search term after each one and wrap it in a span with a class 'highlight' unless it is the last one.
for (let i = 0; i < split.length; i++) {
if (i === split.length - 1) {
ttt += split[i];
} else {
ttt += `${split[i]} <span class="highlight">${searchTerm}</span>`;
}
}
//return the string as HTML.
return ttt;
}
Use it in your HTML (in place of just inserting the string)
<p dangerouslySetInnerHTML={{
__html: this.createHighlight("hello here hello is text hello")}}
/>
And remember to add a class to style the highlighted text (.highlight {bacround-color: yellow})
Here is a link to a codepen https://codesandbox.io/s/flamboyant-frost-hn47k?fontsize=14
Using javascript
var searchTerm = "hello";
function heighlightText() {
var ele= document.getElementById("sample");
var arrText = ele.innerHTML.split(searchTerm);
var rst = "";
for(i=0;i<arrText.length;i++) {
if(i === arrText.length - 1)
{
rst +=arrText[i];
continue;
}
rst +=arrText[i] + "<span class='highlight'>" + searchTerm +"</span>";
}
ele.innerHTML = rst;
}
heighlightText();
.highlight {
background-color : yellow;
}
<div id="sample">hello test search the things hello and then</div>
You can try this, see if it helps:
class App extends React.Component{
highlightText(sentence, wordToHighlight){
let highlightedText = sentence.split(" ")
.map(word => word.toUpperCase() === wordToHighlight.toUpperCase() ? `<b>${word}</b>` : word)
.join(" ");
return {__html: highlightedText};
}
render(){
return(
<div dangerouslySetInnerHTML={this.highlightText("I am a robot charlie.", "robot")} />
)
}
}
ReactDOM.render( <App />, document.getElementById('root') );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

React--Div exists, but is empty & more problems

I'm using the code below to pull in a list of data from a JSON file in order to populate a webpage with News. However, with what I have, the div is empty when I inspect it, and I'm not sure why. When I attempt other solutions, I get errors or the same output.
const newsList = labNewsJson['news']
class News extends Component {
render() {
const news = newsList.map((newsItem) => {
<div>{newsItem}</div>
});
return (
<div className='container'>
<h1>Lab News</h1>
<div>{news}</div>
</div>
);
}
}
export default News;
You need to add a return to your map function.
const news = newsList.map((newsItem, index) => {
return <div key={index}>{newsItem.title}</div>
});
When you are using {}, map function does not return anything. You have two options:
1- Try to use () instead of {}:
const news = newsList.map((newsItem) => (
<div>{newsItem}</div>
))
2- Return the item in every iteration:
const news = newsList.map((newsItem) => {
return <div>{newsItem}</div>
})

How to use Observables/ real-time change only one entry in table in angular2+

I am stuck with an issue in angular4 and node.js app. I display the data in a table that has 7 columns via *ngFor . Now the issue is , i need to dynamically and on real-time basis update the last column . i.e. if the column -> Active ... is Yes , then i display a green color and when the Active ...is No, then i display a red color. But then i am not sure how to update the last column and only the last column real-time.
[1]
I thought of using Observables code in init from where i call the table data, but that will only keep on showing the loader always , as the data will keep on uploading regularly after a few seconds and disturb the entire table view.
[2]
And i have shown the color based on an if condition, but in all the entries, it shows only green color.
code -
interface.component.ts
export class MasterInterfaceComponent implements OnInit {
activeCheck;
activeValue;
activeRedColor;
activeGreenColor;
ngOnInit() {
console.log("beginning of func");
Observable.interval(15000).subscribe(x => {
console.log("Called")
this.viewData();
});
}
viewData() {
this.loading = true;
var url = config.url;
var port = config.port;
this.http.post("http://" + url + ":" ...... ('Token') }) })
.map(result => this.result = result.json(),
)
.subscribe((res: Response) => {
this.loading = false;
this.records = res;
console.log("xxxx interface view result data ",this.result)
console.log("XXXXXXXXXXXX interface view res data ", res);
this.activeCheck = this.result;
for (let obj of this.activeCheck){
for (let key in obj){
if (key == "ACTIVE_YN"){
if (obj[key] == "Y"){
this.activeRedColor = false;
this.activeGreenColor = true;
console.log("this.activeGreenColor = true;");
}
else if (obj[key] == "N"){
this.activeRedColor = true;
this.activeGreenColor = false;
console.log("this.activeGreenColor = false;");
}
}
}
}
});
}
interface.component.html
<tbody>
<tr *ngFor="let data of result |filter:filter| orderBy : 'IF_ID'|
paginate: { itemsPerPage: 5, currentPage: p }; let i =
index">
<td class="text-center" style="width:8%">
<a [hidden]= "accessIdHide" [routerLink]="['/master-system/update-
interface']" (click)="forUpdate(data)" data-toggle="tooltip"
title="Update" style="color:#ffffff;;float:left"
type="link">
<span class="glyphicon glyphicon-plus-sign"></span>
</a>{{data.IF_ID}}
</td>
<td>{{data.IF_CD}}</td>
<td>{{data.IF_NAME}}</td>
<td>{{data.IF_DESCRIPTION}}</td>
<td>{{data.SRC_SYS_NAME}}</td>
<td>{{data.TRGT_SYS_NAME}}</td>
<td >
<img [hidden]= "activeGreenColor" src = "url" width="10" height =
"10">{{data.ACTIVE_YN}}
<img [hidden] = "activeRedColor" src = "url" width="10" height =
"10">{{data.ACTIVE_YN}}
</td>
</tr>
</tbody>
First of all, you do not need two switches, but the way you had it is ok, but for our discussion, let's just say we have one switch "isActive".
I think your code should work, but you need to make sure the subscription is firing, ex.
.subscribe((res: Response) => {
console.log(res);
If this code fires every time the data changes, then your img should be turned on and off
<img *ngIf="isActive" src="url">
or
<img [ngClass]="{ isActive: green }">
Either of the above is fine. So just try to debug your observable to work the way you wanted, ex. when it should be fired and in which frequency.

Render HTML from array items

I am attempting to render a list of HTML elements (links) stored in an array.
I am initially constructing the array as such:
const availableSizes = product.simples.filter((value) => {
return value.stockStatus === STATUS_AVAILABLE;
}).map((value, index) => {
return `${value.filterValue} `;
});
An example of the array contents is :
["35 ", "36 ", "36.5 ", "37.5 ", "38 ", "39 ", "39.5 ", "40 ", "41 ", "41.5 ", "42 ", "42.5 ", "43 ", "44 ", "44.5 ", "45 ", "46 ", "46.5 ", "48 ", "49 "]
I attempted to modify how each string is built as such:
const availableSizes = product.simples.filter((value) => {
return value.stockStatus === STATUS_AVAILABLE;
}).map((value, index) => {
return `${value.filterValue}`;
});
but the HTML was escaped and printed directly in the output without it being parsed as HTML but as a common string.
Please note that not only I need to render the links but I also need to have onClick handlers that do specific actions (save a cookie for example), so the links need to be handled by React as well.
In .map you return String however you should return JSX
const availableSizes = product.simples.filter((value) => {
return value.stockStatus === STATUS_AVAILABLE;
}).map((value, index) => {
return <a key={ index } href="#">{ value.filterValue }</a>;
});
As you have JSX available you could do the following instead:
class MyComponent extends React.Component {
render() {
const availableSizes = product.simples
.filter((value) => value.stockStatus === STATUS_AVAILABLE)
.map((value, index) => <a key={index} href="#">${value.filterValue}</a>);
return (
<div>
{availableSizes}
</div>
);
}
}
Pay attention to the key={index} that I added. React needs this to optimise the rendering process. If you have a unique id for each product you could use that instead for a better optimisation. It's used in React's diffing algorithm. For example: <a key={value.id} href="#">${value.filterValue}</a>