I am using Next.js and Typescript to create a page based on the URL. Using the Next.js catch all route ('[[...slug]]'), I would like to create the page based on how many objects the URL contains.
'/demo' would create a page with generic data
'/demo/category' would create a page with category-level data
'/demo/category/subcategory' would create a page with detailed data based on the subcategory
my file 'pages/demo/[[...slug]].tsx' contains the following
import { useRouter } from "next/router";
import { Fragment } from "react";
import DemoHeader from "../../components/layout/demoheader";
function DemoPage() {
const router = useRouter();
let arrayCount = 0;
let storyData = new Array();
storyData = router.query.slug;
arrayCount = storyData.length;
let tempType = "";
let tempBrand = "";
switch (arrayCount) {
case 0: {
tempType = "generictype";
tempBrand = "genericbrand";
break;
}
case 1:
tempType = storyData[0];
break;
case 2:
tempType = storyData[0];
tempBrand = storyData[1];
break;
}
/* if (!storyData) {
const storyType = "smallbusiness";
const storyBrand = "ountybank";
} else {
const storyType = storyData[0];
const storyBrand = storyData[1];
}
*/
const storyType = tempType;
const storyBrand = tempBrand;
return (
<Fragment>
<DemoHeader storybrand={storyBrand} storytype={storyType} />
<main className="container">
<div className="p-4 p-md-5 mb-4 rounded text-bg-dark">
...
</div>
</main>
</Fragment>
);
}
export default DemoPage;
The above will not work because I can't figure out a way to define storyData as an array, and then assign it to router.query.slug.
Originally, I tested storyData and it worked. However, when I passed those variables to compontents it appeared to redefine the variable to the default in an odd way.
What is the best way to look at the router.query.slug, and create two variables from the url that you can pass into components?
Thanks!
Related
I'm pretty new to React JS and am creating a generic Excel Comparison Tool. The tool takes 2 EXCEL files, converts them into JSON arrays, and feeds them into a material table with added radio buttons for sheet traversal (ex. A file has sheet 1 and sheet 2). I have a compare button that takes those json arrays and compares their row contents and Edit places the results in a seperate useState array called rowColors but am confused on how to add styles to the material table rows when a match is found. When I try to use rowColors as the rowStyle parameter in options, the console prints rowData instead. Can anyone help?
This is the Compare Button Function:
const compareTables = (obj1,obj2)=>{
const _ = require('lodash');
const data1 = Object.values(obj1);
const data2 = Object.values(obj2);
const results = [];
if (!data1.length||!data2.length){
alert('Missing Table Data')
}else{
//if table 1 has more cells than table 2
if(data1.length>=data2.length){
for (let i=0;i<data1.length;i++){
if (_.isEqual(data1[i],data2[i])){
console.log("row match, table 1 bigger")
results.push({color: "1"});
}
else{
console.log("row does not match, table 1")
results.push({color: "2"});
}
}
}else{
for (let i=0;i<data2.length;i++){
if (_.isEqual(data2[i],data1[i])){
console.log("row match, table 2 bigger")
results.push({color: "1"});
}
else{
console.log("row does not match, table 2 bigger")
results.push({color: "2"});
}
}
}
setRowColors(results)
console.log(results)
}
}
Options
const options = {
rowStyle: rowColors => { //row that is most likely the problem, putting brackets around rowColors leads to problem with rowStyle
console.log(rowColors)
if(rowColors.color ==='1'){
return {
backgroundColor: "green",
};
}
else if (rowColors.color==='2'){
return {
fontFamily: "Mulish-Regular",
backgroundColor: "red",
};
}
else{
return{
fontFamily: "Mulish-Regular",
backgroundColor: "#fff",
};
}
}
}
How Im rendering the Material Table
<div class="excelSpreadsheet1">
<Paper className='excelPaper1' elevation={10}>
{sheetNames1?.map((s,index) =>
<input type="radio" name="sheetNames1" value={s} key={index} onClick={()=>pageController1(file1,index)}></input>)}
<h3 class='tabName1'>{thisSheet1}</h3>
<MaterialTable id="mat1" title={fileName1} data={excelData1} columns={colDefs1} options={options}></MaterialTable>
</Paper>
</div>
How am filling excelData and colDefs which are initialized as empty useStates:
const importExcel1 = (e)=>{
const file = e.target.files[0]
setFile1(file)
setFileName1(file.name)
console.log(file)
const reader= new FileReader()
reader.onload=(event)=>{
//data parsing
const bstr = event.target.result
const workBook=XLSX.read(bstr,{type:"binary"})
setSheetNames1(workBook.SheetNames)
//get first sheet
const workSheetName=workBook.SheetNames[0]//throw method here
setThisSheet1(workSheetName)
const workSheet=workBook.Sheets[workSheetName]
const fileData = XLSX.utils.sheet_to_json(workSheet,{header:1})
//split data into 2 parts
const headers=fileData[0]
const heads=headers.map(head=>({title:head,field:head}))
console.log(heads)
setColDefs1(heads)
fileData.splice(0,1)
setExcelData1(convertToJson(headers,fileData))
}
reader.readAsBinaryString(file)
}
What the compare button looks like:
<button className='compareButton' onClick={() =>compareTables(excelData1,excelData2)}> Compare</button>
I wrote a vue3 component which uses the VirtualScroller from PrimeVue and I would like to scroll to the end of the scroller each time I'm adding new elements. For that, there is scrollInView method which is defined on the component and documented here
My code looks like this (it's typescript with vue-class-component and single file syntax):
<template>
...
<VirtualScroller :items="content" :itemSize="50" class="streamscroller" ref="streamscroller">
<template v-slot:item="{ item }">
<pre>{{ item }}</pre>
</template>
</VirtualScroller>
...
</template>
<script lang="ts">
...
import { ref, ComponentPublicInstance } from "vue";
import VirtualScroller from "primevue/virtualscroller";
...
#Options({
components: {
VirtualScroller,
...
},
})
export default class StreamResultViewer extends Vue {
streamscroller = ref<ComponentPublicInstance<VirtualScroller>>();
content: string [] = [ "No output" ];
...
mounted(): void {
...
console.debug("scroller mounted: ", this.streamscroller.value); // <=== here, already the value is indefined
}
onData(msg: string): void {
const lines = msg.split('\n');
const content = [...this.content, ...lines];
this.content = content;
console.debug("scroller: ", this.streamscroller.value); // <== always undefined
this.streamscroller.value?.scrollInView(this.content.length, 'to-end', 'smooth'); // <== so never called
}
...
The virtual scroller works well (I can add lines each time they arrives and the scroll bar moves...) but I can never call the scroll method because the ref is undefined...
I'd be very grateful for any clue...
Thank you
The only workaround I found is too use $refs like this:
onData(msg: string): void {
const lines = msg.split('\n');
const content = [...this.content, ...lines];
this.content = content;
const scroller = this.$refs.streamscroller as VirtualScroller;
scroller.scrollInView(this.content.length, 'to-end', 'smooth');
}
This way, I am able to call the scrolling method and it works fine.
If someone can explain how it should work normally with ref<T>() in the vue-class-component + typescript mode, I'd be glad to hear that.
I'm creating a mechanism for defining and calculating my own reusable grids. Here's an example of what's returned
[left-bleed-start] 10.245000000001024px [left-bleed-end content-col-start] 1331.8500000001332px [content-col-end right-bleed-start] 10.245000000001024px [right-bleed-end]/[top-bleed-start] 10.245000000001024px [top-bleed-end link-row-start] 81.9600000000082px [content-row-end footer-row-start] 163.9200000000164px [footer-row-end bottom-bleed-start] 10.245000000001024px [bottom-bleed-end]
When applying it like this
<div class="appCompGrid" [style.grid]="Grid.GridCode"></div>
I get the santization warning. However if I copy and paste the value in like this
<div class="appCompGrid" style="grid: (same code);"></div>
everything works. The css class is where I define the display as grid seeing that it'll be consistent no matter what size the screen is. The only thing I could think to do was go back into the function and add + ';' to the end of where the grid code is put together figuring maybe that was throwing something off but it still gives the same error. I tried applying display: grid; inline to see if maybe there was a problem with it reading both from a css class and inline for some odd reason.
I'm using #HostListener to re-calculate the grid as the size or orientation changes, so far I haven't run into a problem with Angular functioning in this manner so I don't understand where to begin with figuring out why this is happening. Here's how I have my component classes set up.
Base Class
export class GridBuilder {
Settings : GridInit = new GridInit();
GridData : Array<GridType> = new Array();
Grid : GridOutput = new GridOutput();
constructor() { this.GridData = GridDefs; }
public buildGrid() {
const coreSettings : GridInit = this.Settings;
const gridData : GridType[] = this.GridData;
const w: number = multiply( coreSettings.Size.Width, coreSettings.Size.PixelRatio );
const h: number = multiply( coreSettings.Size.Height, coreSettings.Size.PixelRatio );
const o: string = checkOrientation( w, h );
const c: CellSpecs = calcCell( o, w );
const t: GridType = gridData.find( a => a.GridName == coreSettings.GridStyle );
const cols: string = calcArea( t.Columns, c );
const rows: string = calcArea( t.Rows, c );
this.Grid.GridCode = cols + '/' + rows + ';';
this.Grid.GridAreas = t.Areas;
}
}
Secondary class for app component/ any top tier container
export class SiteGrid extends GridBuilder {
constructor(){
super();
this.applySizeSettings();
}
applySizeSettings(){
this.Settings.Size.Width = window.innerWidth;
this.Settings.Size.Height = window.innerHeight;
this.Settings.Size.PixelRatio = window.devicePixelRatio;
}
}
the AppComponent
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent extends SiteGrid {
title = 'app';
#HostListener( 'window: resize', [ '$event' ] )onResize( event ){ this.applySizeSettings(); this.buildGrid(); }
constructor(){
super();
this.Settings.GridStyle = 'SiteGridA';
this.buildGrid();
}
}
I don't know how relevant this may be in helping figure out the solution but thought I'd show how things are flowing just incase. Anyone know why this warning is occurring?
You need to implement a sanitizer to cleanse your css, or bypass it...
constructor(private sanitizer: DomSanitizer) {
this.sanitizedCSS = sanitizer.bypassSecurityTrustStyle(Grid.GridCode) ;
}
As for why, this blog explains it pretty well, as does the DomSanitizer documentation.
DomSanitizer helps preventing Cross Site Scripting Security bugs (XSS) by sanitizing values to be safe to use in the different DOM contexts.
Just as a preface, I am using create-react-app boiler plate.
I am trying to parse out a post from the wordpress API into a react component so I can extract all images and make them their own JSX objects. I need to have each post have its own on click method so I cant just use the dangerouslyaddHTML function in React.
Currently, I search through the string of json that had the rendered HTML from the WP content of each post and find all img tags. I then add then image src to an array and set this in my state.
However I am getting this error :
DOMException: Only secure origins are allowed
The site I have this currently hosted on it http://natalie.madeline-omoore.com/build/
The code for part of my component is below:
`componentDidMount(){
var owner;
for(var i = 0; i < this.props.categoryId.length; i++) {
if (this.props.categoryId[i].num === this.props.content.categories[0]) {
owner = this.props.categoryId[i].category;
this.setState({
catname: owner
})
break;
}
}
var theContent = this.props.content.content.rendered;
var output = $(theContent).find("img").map(function() {
return this.src;
}).get();
this.setState({
postImages: output
});
}
click(){
this.updateActive();
this.props.fix();
}
render() {
if (this.props.className === this.state.catname) {
return (
<div >
{ this.props.className === this.state.catname &&
<article className={`${this.state.catname}`} >
{this.state.postImages.map((image, i) =>
<ImageContain key={image} image={image} fix={this.props.fix} />
)}
</article>
}
</div>
);
} else {
return null;
}
}`
I am new to react so thank you!
This sounds like an issue with the 'service worker' that is default with create-react-app
You can deregister the worker by adding the following to index.js:
import { unregister } from './registerServiceWorker';
// ...
unregister();
Be sure to call unregister() after registerServiceWorker() is called.
You can learn more about this here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#making-a-progressive-web-app
Good luck, and happy coding!
I'm doing a very simple react+redux application where I've a reducer called goals and a container called GoalsContainer.
From App Container I call the action goal for load the initial goals from a local db(indexedDB)
dispatch(loadGoals(currentDate));
This call the loadGoals from the goals actions:
export function loadGoals(currentDate = new Date()){
return dispatch => {
var goals = getGoalsFromDB(normalizeDate(currentDate)); // with this I get an array from the db
dispatch(setLoadGoals(goals));
}
}
function setLoadGoals(goals) {
return {
type: types.LOAD_GOALS,
goals
};
}
And then in my reducer I've this:
export default function goals(state = [], action) {
switch(action.type) {
case types.LOAD_GOALS:
return action.goals; // here I set the state of the goal reducer with the array passed via action
default:
console.log('Im here');
return state;
}
}
and this is my GoalsContainer(read the comments in code):
class GoalsContainer extends React.Component {
render() {
if (this.props.goals != undefined) {
console.log('ok called the render'); // in chrome console shows it
console.log(this.props.goals); // in chrome console shows correctly the goals loaded
console.log(this.props.goals.length); // it say 2
if (this.props.goals.length > 0) { // here fails...
console.log('good');
console.log(this.props.goals);
var goalsView = <div>There are goals</div>
}
else {
console.log('why go here?'); // go here
console.log(this.props.goals);
var goalsView = <div>No goals</div>
}
} else {
var goalsView = <div>Undefined</div>
}
return (
<div id="goals-main">
{goalsView}
</div>
);
}
}
GoalsContainer.propTypes = propTypes;
function mapStateToProps(state) {
const { goals, environment } = state;
const { currentDate } = environment;
return {
goals,
currentDate
}
}
export default connect(mapStateToProps)(GoalsContainer);
The problem is that when it does the if check, it fails(like if there are 0 goals), but in chrome console show correctly the goals array...
Then if I force with some workaround the render(), all works correctly.
What I've done wrong ?
You didn't mention if you use https://github.com/gaearon/redux-thunk or not. To use reducer returning function you should definitely install it.
It's hard to follow all of the parts of your code from random gists. What happens if you change your GoalsContainer to be;
class GoalsContainer extends React.Component {
render() {
console.log(this.props.goals);
return (
<div id="goals-main">
{(this.props.goals.length >= 1)?<div>There are goals</div>:<div>Nope!</div>}
</div>
);
}
}
What gets logged to the console?