Hello everyone I need help regarding linking of pages in nextjs.
actually I know how to link but what i want is following:
I have my home page having course team contact links in navbar so when I click course then course page gets open with url "localhost:3000/course" and in that course page I have courses .
I want that by clicking on any course in course page it should get open and the url should be "localhost:3000/course/course_1".
what should I do ?
This is header component:
const Header = () => (
<div>
<nav className="navbar navbar-expand-lg navbar-dark" >
<Logo />
<button className="navbar-toggler" type="button" data-target="#navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse">
<ul className="navbar-nav">
<li>
<a href="/" className="nav-link" >Home</a>
</li>
<li>
<a href="/team" className="nav-link" >Team</a>
</li>
<li>
<a href="/courses" className="nav-link" >Course</a>
</li>
<li >
<a href="/contact" className="nav-link" >Contact</a>
</li>
</ul>
<form className="form-inline my-2 my-lg-0">
<div className="d-flex justify-content-center h-100">
<div className="searchbar">
<input className="search_input text-center" type="text" name="" placeholder="Search..." />
<i className="fas fa-search"></i>
</div>
</div>
</form>
</div>
</nav>
This is the course :
const Course = () => (
<div>
<div className="col-xs-12 col-sm-4">
<div className="card">
<a className="img-card img-part-2" href="#">
<img src="/static/course1-img.jpg" />
</a>
<div className="teacher-img">
<div className="ava">
<img alt="Admin bar avatar" src="http://ivy-school.thimpress.com/demo-3/wp-content/uploads/learn-press-profile/5/2448c53ace919662a2b977d2be3a47c5.jpg" className="avatar avatar-68 photo" height="68" width="68" />
</div>
</div>
<div className="card-content">
<p className="card-para">Charlie Brown </p>
<h4 className="card-title">
<a href="/Pyhton">
Learn Python – Interactive <br/> Python
</a>
</h4>
<div className="info-course">
<span className="icon1&-txt">
<i className="fas fa-user"></i>
3549
</span>
<span className="icon2&-txt">
<i className="fas fa-tags"></i>
education
</span>
<span className="icon3&-txt">
<i className="fas fa-star"></i>
0
</span>
</div>
</div>
</div>
</div>
You can try like this:
server.js
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('/course', (req, res) => {
return app.render(req, res, '/courses')
})
server.get('/course/:id', (req, res) => {
return app.render(req, res, '/course', { id: req.params.id })
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
course.js
import React, { Component } from 'react'
export default class extends Component {
static getInitialProps ({ query: { id } }) {
return { courseId: id }
}
render () {
return (
<div>
<h1>Course {this.props.courseId}</h1>
</div>
)
}
}
courses.js
import React, { Component } from 'react'
export default class extends Component {
render () {
return (
<div>
<a href="/course/python">
Learn Python – Interactive <br/> Python
</a>
<a href="/course/javascript">
Learn Javascript – Interactive <br/> Javascript
</a>
</div>
)
}
}
Related
I am making a newsapp i want it to have different categories, instead of using react-router-dom for navigation what i want to do is create a state object and create a key in it named category and set current category as it's value and i have sent that key as a props to news component where i fetch news and embed that category to the fetch URL
I have made a function to set the category and its in app.js I have sent it to navbar component as props, the issue i am facing is that i can't select a category, because for some reason the onClick of the very last category is being called continuously and I know this because I console logged the category in setCategory function, can anyone tell me why this is happening
code in app.js:
import './App.css';
import React, { Component } from 'react'
import Navbar from './components/Navbar';
import News from './components/News';
export default class App extends Component {
constructor() {
super()
this.state = {
darkMode: "light",
country: "us",
category: "general",
key: "general"
}
}
setCategory = (cat)=> {
this.setState({
category: cat
})
console.log(this.state.category)
}
setCountry = (cntry)=> {
this.setState({
category: cntry
})
}
setDarkMode = () => {
if (this.state.darkMode === "light") {
this.setState({ darkMode: "dark" })
document.body.style.backgroundColor = "black"
} else {
this.setState({ darkMode: "light" })
document.body.style.backgroundColor = "white"
}
}
render() {
return (
<div>
<Navbar setCategory={this.setCategory} setCountry={this.setCountry} setDarkMode={this.setDarkMode} darkMode={this.state.darkMode} />
<News key={this.state.category} category={this.state.category} country={this.state.country} pageSize={18} darkMode={this.state.darkMode} />
</div>
)
}
}
code in Navbar component:
import React, { Component } from 'react'
export default class Navbar extends Component {
constructor(props) {
super(props)
this.setCategory = this.props.setCategory
}
render() {
return (
<div>
<nav className={`navbar navbar-expand-lg navbar-${this.props.darkMode} bg-${this.props.darkMode}`}>
<a className="navbar-brand" href="/">NewsMonkey</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mr-auto">
<li className="nav-item active">
<a className="nav-link" href="/">Home <span className="sr-only">(current)</span></a>
</li>
<li className="nav-item">
<a className="nav-link" href="/about">About</a>
</li>
<li className="nav-item dropdown">
<a className="nav-link dropdown-toggle" href="/" role="button" data-toggle="dropdown" aria-expanded="false">
Categories
</a>
<div className="dropdown-menu">
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("business")}>Business</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("science")}>Science</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("technology")}>Technology</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("entertainment")}>Entertainment</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("health")}>Health</p>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("sports")}>Sports</p>
<div className="dropdown-divider"></div>
<p className="dropdown-item cursor-pointer" onClick={this.setCategory("general")}>General</p>
</div>
</li>
</ul>
{/* <form className="form-inline my-2 my-lg-0">
<input className="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" />
<button className="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form> */}
<div className={`custom-control custom-switch text-${this.props.darkMode === "light" ? "dark" : "light"}`}>
<input type="checkbox" className="custom-control-input" onClick={this.props.setDarkMode} id="customSwitch1" />
<label className={`custom-control-label`} htmlFor="customSwitch1">Dark mode</label>
</div>
</div>
</nav>
</div>
)
}
}
This doesn't do what you think it does:
onClick={this.setCategory("business")}
This calls the setCategory function immediately and uses the result of that function as the onClick handler. Don't pass the result of calling a function to onClick, pass a function itself:
onClick={() => this.setCategory("business")}
import React, { useEffect } from "react";
import Loader from "../layout/Loader";
import { useAlert } from "react-alert";
import { useDispatch, useSelector } from "react-redux";
import { getProductDetails, clearErrors } from "../../actions/productActions";
const ProductDetails = ({ match }) => {
const dispatch = useDispatch();
const alert = useAlert();
const { loading, error, product } = useSelector((state) =>
state.productDetails()
);
useEffect(() => {
dispatch(getProductDetails());
if (error) {
alert.error(error);
dispatch(clearErrors());
}
}, [dispatch, alert, error]);
return (
<>
{!loading ? (
<Loader />
) : (
<>
<div className='row f-flex justify-content-around'>
<div className='col-12 col-lg-5 img-fluid' id='product_image'>
<img
src='https://i5.walmartimages.com/asr/1223a935-2a61-480a-95a1-21904ff8986c_1.17fa3d7870e3d9b1248da7b1144787f5.jpeg?odnWidth=undefined&odnHeight=undefined&odnBg=ffffff'
alt='sdf'
height='500'
width='500'/>
</div>
<div className='col-12 col-lg-5 mt-5'>
<h3>{product.name}</h3>
<p id='product_id'>Product # sklfjdk35fsdf5090</p>
<hr />
<div className='rating-outer'>
<div className='rating-inner'></div>
</div>
<span id='no_of_reviews'>(5 Reviews)</span>
<hr />
<p id='product_price'>$108.00</p>
<div className='stockCounter d-inline'>
<span className='btn btn-danger minus'>-</span>
<input
type='number'
className='form-control count d-inline'
value='1'
readOnly
/>
<span className='btn btn-primary plus'>+</span>
</div>
<button
type='button'
id='cart_btn'
className='btn btn-primary d-inline ml-4'
>
Add to Cart
</button>
<hr />
<p>
Status: <span id='stock_status'>In Stock</span>
</p>
<hr />
<h4 className='mt-2'>Description:</h4>
<p>
Binge on movies and TV episodes, news, sports, music and more!
We insisted on 720p High Definition for this 32" LED TV,
bringing out more lifelike color, texture and detail. We also
partnered with Roku to bring you the best possible content with
thousands of channels to choose from, conveniently presented
through your own custom home screen.
</p>
<hr />
<p id='product_seller mb-3'>
Sold by: <strong>Amazon</strong>
</p>
<button
id='review_btn'
type='button'
className='btn btn-primary mt-4'
data-toggle='modal'
data-target='#ratingModal'
>
Submit Your Review
</button>
<div className='row mt-2 mb-5'>
<div className='rating w-50'>
<div
className='modal fade'
id='ratingModal'
tabIndex='-1'
role='dialog'
aria-labelledby='ratingModalLabel'
aria-hidden='true'
>
<div className='modal-dialog' role='document'>
<div className='modal-content'>
<div className='modal-header'>
<h5 className='modal-title' id='ratingModalLabel'>
Submit Review
</h5>
<button
type='button'
className='close'
data-dismiss='modal'
aria-label='Close'
>
<span aria-hidden='true'>×</span>
</button>
</div>
<div className='modal-body'>
<ul className='stars'>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
<li className='star'>
<i className='fa fa-star'></i>
</li>
</ul>
<textarea
name='review'
id='review'
className='form-control mt-3'
></textarea>
<button
className='btn my-3 float-right review-btn px-4 text-white'
data-dismiss='modal'
aria-label='Close'
>
Submit
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)}
</>
);
};
export default ProductDetails;
As the error suggests
state.productDetails is not a function
This is because productDetails is a reducer object and Not a function created by redux. Hence, your code should be
const { loading, error, product } = useSelector((state) =>state.productDetails); //Note: productDetails without '()'
Hope it helps.
Side Note: While posting any question or answer please format the code properly next time so it's readable. :)
The data variable contains all the data needed for this operation the problem is that the frontend shows card one below the other whereas I want it to show 3 or 4 in one row. I can assure that the error is not with the react or the graphql, the error is in the bootstrap or the way I am rendering the data.
I just want a responsive design so at first i have created a html css bootstrap ui which was perfectly working and was responsive but when i combined it with the data it lost its responsiveness and now it shows cards one below the other.
here is the image of how it is currentlyIt shows that there is a card but no other card along the row
Here is my code:
import React, { useState } from "react";
import { gql, useQuery } from "#apollo/client";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
const getBooksQuery = gql`
{
books {
name
id
genre
author {
name
}
description
rating
image
}
}
`;
function BooksDisplay() {
const { loading, error, data } = useQuery(getBooksQuery);
var [selected, setSelected] = useState("");
if (loading) return <p>Loading....</p>;
if (error) return <p>Ops! Something went wrong</p>;
return (
<div>
<div id="book-list">
{data.books.map((book) => (
<div className="container-fluid my-5 books_section">
<div className="row">
<div className="col-xl-3 col-lg-4 col-sm-6 col-12 mt-4">
<div className="card h-100">
<img src={book.image} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title font-weight-bold text-secondary">
{book.name}
</h5>
<span className="card-text">
{book.description}
<div className="collapse m-0" id="collapseExample">
<div className="card card-body border-0 p-0">
{book.description}
</div>
</div>
</span>
<a
className="card-link d-block"
data-toggle="collapse"
href="#collapseExample"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
See More
</a>
</div>
<ul className="list-group list-group-flush">
<li className="list-group-item">
Authors:
<span>
{book.author.map((author) => author.name).join(" ")}
</span>
</li>
<li className="list-group-item">
Genre: <span>{book.genre.join(" ")}</span>
</li>
<li className="list-group-item">
Ratings: <span>{book.rating}</span>
</li>
</ul>
</div>
</div>
</div>
</div>
))}
</div>
{/* <BookDetail bookid={selected} /> */}
</div>
);
}
function BookList() {
return (
<div>{BooksDisplay()}</div>
);
}
export default BookList;
You need to iterate the columns instead of the container...
import React, { useState } from "react";
import { gql, useQuery } from "#apollo/client";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
const getBooksQuery = gql`
{
books {
name
id
genre
author {
name
}
description
rating
image
}
}
`;
function BooksDisplay() {
const { loading, error, data } = useQuery(getBooksQuery);
var [selected, setSelected] = useState("");
if (loading) return <p>Loading....</p>;
if (error) return <p>Ops! Something went wrong</p>;
return (
<div>
<div id="book-list">
<div className="container-fluid my-5 books_section">
<div className="row">
{data.books.map((book) => (
<div className="col-xl-3 col-lg-4 col-sm-6 col-12 mt-4">
<div className="card h-100">
<img src={book.image} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title font-weight-bold text-secondary">
{book.name}
</h5>
<span className="card-text">
{book.description}
<div className="collapse m-0" id="collapseExample">
<div className="card card-body border-0 p-0">
{book.description}
</div>
</div>
</span>
<a
className="card-link d-block"
data-toggle="collapse"
href="#collapseExample"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
See More
</a>
</div>
<ul className="list-group list-group-flush">
<li className="list-group-item">
Authors:
<span>
{book.author.map((author) => author.name).join(" ")}
</span>
</li>
<li className="list-group-item">
Genre: <span>{book.genre.join(" ")}</span>
</li>
<li className="list-group-item">
Ratings: <span>{book.rating}</span>
</li>
</ul>
</div>
</div>
))}
</div>
</div>
</div>
{/* <BookDetail bookid={selected} /> */}
</div>
);
}
function BookList() {
return (
<div>{BooksDisplay()}</div>
);
}
export default BookList;
Just put these 2 tags out of the loop
< div className="container-fluid my-5 books_section">
< div className="row">
And it will work.
we are building an e-commerce website using React and Redux as a front-end. And we need to display categories data on the category page. We successfully get JSON data in console, but we have a problem to display in the category component page.
Please help to solve this problem
reducer/categories.js:
import { RECEIVE_CATEGORIES } from "../constants/ActionTypes";
const initialState = {
categories: []
};
const categoryReducer = (state = initialState, action) => {
switch (action.type) {
case RECEIVE_CATEGORIES:
return {
...state,
categories: action.categories
};
default:
return state;
}
};
export default categoryReducer;
action/index.js:
import shop from '../api/shop'
import * as types from '../constants/ActionTypes'
import store from "../store";
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.min.css';
export const fetchProductsBegin = () => ({
type: types.FETCH_PRODUCTS_BEGIN
});
export const fetchCategoryBegin = () => ({
type: types.FETCH_CATEGORIES_BEGIN
});
export const receiveProducts = products => ({
type: types.RECEIVE_PRODUCTS,
products
})
export const receiveCategories = categories => ({
type: types.RECEIVE_CATEGORIES,
categories
})
export const getAllProducts = () => dispatch => {
dispatch(fetchProductsBegin());
return shop.getProducts(
products => {
products.then((data)=> { dispatch(receiveProducts(data));})})
}
export const getAllCategories = () => dispatch => {
dispatch(fetchCategoryBegin());
return shop.getCategories(
products => {
products.then((data)=> {
console.log("executed");
dispatch(receiveCategories(data));})})
}
export const fetchSingleProduct = productId => ({
type: types.FETCH_SINGLE_PRODUCT,
productId
})
//it seems that I should probably use this as the basis for "Cart"
export const addToCart = (product,qty) => (dispatch) => {
toast.success("Item Added to Cart");
dispatch(addToCartUnsafe(product, qty))
}
export const addToCartAndRemoveWishlist = (product,qty) => (dispatch) => {
toast.success("Item Added to Cart");
dispatch(addToCartUnsafe(product, qty));
dispatch(removeFromWishlist(product));
}
export const addToCartUnsafe = (product, qty) => ({
type: types.ADD_TO_CART,
product,
qty
});
export const removeFromCart = product_id => (dispatch) => {
toast.error("Item Removed from Cart");
dispatch({
type: types.REMOVE_FROM_CART,
product_id
})
};
export const incrementQty = (product,qty) => (dispatch) => {
toast.success("Item Added to Cart");
dispatch(addToCartUnsafe(product, qty))
}
export const decrementQty = productId => (dispatch) => {
toast.warn("Item Decrement Qty to Cart");
dispatch({
type: types.DECREMENT_QTY,
productId})
};
//it seems that I should probably use this as the basis for "Wishlist"
export const addToWishlist = (product) => (dispatch) => {
toast.success("Item Added to Wishlist");
dispatch(addToWishlistUnsafe(product))
}
export const addToWishlistUnsafe = (product) => ({
type: types.ADD_TO_WISHLIST,
product
});
export const removeFromWishlist = product_id => (dispatch) => {
toast.error("Item Removed from Wishlist");
dispatch({
type: types.REMOVE_FROM_WISHLIST,
product_id
})
};
//Compare Products
export const addToCompare = (product) => (dispatch) => {
toast.success("Item Added to Compare");
dispatch(addToCompareUnsafe(product))
}
export const addToCompareUnsafe= (product) => ({
type: types.ADD_TO_COMPARE,
product
});
export const removeFromCompare = product_id => ({
type: types.REMOVE_FROM_COMPARE,
product_id
});
// Filters
export const filterBrand = (brand) => ({
type: types.FILTER_BRAND,
brand
});
export const filterColor = (color) => ({
type: types.FILTER_COLOR,
color
});
export const filterPrice = (value) => ({
type: types.FILTER_PRICE,
value
});
export const filterSort = (sort_by) => ({
type: types.SORT_BY,
sort_by
});
// Currency
export const changeCurrency = (symbol) => ({
type: types.CHANGE_CURRENCY,
symbol
});
Api/shop.js:
/**
* Mocking client-server processing
*/
import axios from "axios";
// import _products from './data.json'
import React, { useState, useEffect } from "react";
import store from "../store";
import { receiveProducts } from "../actions/index";
const TIMEOUT = 100;
const _products = axios
.get(`http://localhost:4000/product`)
.then((response) => {
return response.data;
});
const _category = axios
.get(`http://localhost:4000/categories`)
.then((response) => {
return response.data;
});
export default {
getProducts: (cb, timeout) =>
setTimeout(() => cb(_products), timeout || TIMEOUT),
getCategories: (cb, timeout) =>
setTimeout(() => cb(_category), timeout || TIMEOUT),
buyProducts: (payload, cb, timeout) =>
setTimeout(() => cb(), timeout || TIMEOUT),
};
element-category.jsx:
import React, { Component } from "react";
import Slider from "react-slick";
import Breadcrumb from "../../common/breadcrumb";
import { Slider6, Slider4 } from "../../../services/script";
import "react-tabs/style/react-tabs.scss";
import { connect } from "react-redux";
import { getAllCategories } from "../../../actions";
class ElementCategory extends Component {
constructor(props) {
super(props);
this.state = {
categories: this.props.categories
};
}
componentDidMount() {
this.props.getAllCategories();
}
render() {
console.log(this.state.categories);
return (
<div>
<Breadcrumb parent={"Elements"} title={"Category"} />
{/*Category One*/}
<div className="container">
<section className="section-b-space">
<div className="row">
<div className="col">
<Slider {...Slider6} className="slide-6 no-arrow">
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat1.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>shoes</h5>
</a>
</div>
</div>
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat2.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>casual shoes</h5>
</a>
</div>
</div>
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat3.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>formal shoes</h5>
</a>
</div>
</div>
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat4.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>flat</h5>
</a>
</div>
</div>
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat5.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>heels</h5>
</a>
</div>
</div>
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat6.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>boots</h5>
</a>
</div>
</div>
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat2.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>casual shoes</h5>
</a>
</div>
</div>
<div className="category-block">
<a href="#">
<div className="category-image">
<img
src={`${process.env.PUBLIC_URL}/assets/images/icon/cat3.png`}
alt=""
/>
</div>
</a>
<div className="category-details">
<a href="#">
<h5>casual shoes</h5>
</a>
</div>
</div>
</Slider>
</div>
</div>
</section>
</div>
{/*Category Two*/}
<section className="p-0 ratio2_1">
<div className="container-fluid">
<div className="row category-border">
<div className="col-sm-4 border-padding">
<div className="category-banner">
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/cat1.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<div className="category-box">
<a href="#">
<h2>men</h2>
</a>
</div>
</div>
</div>
<div className="col-sm-4 border-padding">
<div className="category-banner">
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/cat2.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<div className="category-box">
<a href="#">
<h2>women</h2>
</a>
</div>
</div>
</div>
<div className="col-sm-4 border-padding">
<div className="category-banner">
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/cat3.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<div className="category-box">
<a href="#">
<h2>kids</h2>
</a>
</div>
</div>
</div>
</div>
</div>
</section>
{/*Category Three*/}
<div className="container category-button">
<section className="section-b-space">
<div className="row partition1">
<div className="col">
<a href="#" className="btn btn-outline btn-block">
airbag
</a>
</div>
<div className="col">
<a href="#" className="btn btn-outline btn-block">
burn bag
</a>
</div>
<div className="col">
<a href="#" className="btn btn-outline btn-block">
briefcase
</a>
</div>
<div className="col">
<a href="#" className="btn btn-outline btn-block">
carpet bag
</a>
</div>
<div className="col">
<a href="#" className="btn btn-outline btn-block">
money bag
</a>
</div>
<div className="col">
<a href="#" className="btn btn-outline btn-block">
tucker bag
</a>
</div>
</div>
</section>
</div>
{/*Category Four*/}
<div className="category-bg ratio_square">
<div className="container-fluid p-0">
<div className="row order-section">
<div className="col-sm-4 p-0">
<a href="#" className="image-block">
<img
alt=""
src={`${process.env.PUBLIC_URL}/assets/images/cat1.jpg`}
className="img-fluid blur-up lazyload bg-img"
/>
</a>
</div>
<div className="col-sm-4 p-0">
<div className="contain-block even">
<div>
<h6>new products</h6>
<a href="#">
<h2>zipper storage bag</h2>
</a>
<a href="#" className="btn btn-solid category-btn">
-80% off
</a>
<a href="#">
<h6>
<span>shop now</span>
</h6>
</a>
</div>
</div>
</div>
<div className="col-sm-4 p-0">
<a href="#" className="image-block">
<img
alt=""
src={`${process.env.PUBLIC_URL}/assets/images/cat2.jpg`}
className="img-fluid blur-up lazyload bg-img"
/>
</a>
</div>
<div className="col-sm-4 p-0">
<div className="contain-block">
<div>
<h6>on sale</h6>
<a href="#">
<h2>tucker bag</h2>
</a>{" "}
<a href="#" className="btn btn-solid category-btn">
save 30% off
</a>
<a href="#">
<h6>
<span>shop now</span>
</h6>
</a>
</div>
</div>
</div>
<div className="col-sm-4 p-0">
<a href="#" className="image-block even">
<img
alt=""
src={`${process.env.PUBLIC_URL}/assets/images/cat3.jpg`}
className="img-fluid blur-up lazyload bg-img"
/>
</a>
</div>
<div className="col-sm-4 p-0">
<div className="contain-block">
<div>
<h6>summer sale</h6>
<a href="#">
<h2>gate check bag</h2>
</a>{" "}
<a href="#" className="btn btn-solid category-btn">
minimum 50% off
</a>
<a href="#">
<h6>
<span>shop now</span>
</h6>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
{/*Category Five*/}
<section className="ratio_portrait">
<div className="container">
<div className="row">
<div className="col">
<Slider {...Slider4} className="slide-4 category-m no-arrow">
<div>
<div className="category-wrapper">
<div>
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/watch/cat1.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<h4>watch models</h4>
<ul className="category-link">
<li>
d1 milano
</li>
<li>
damaskeening
</li>
<li>
diving watch
</li>
<li>
dollar watch
</li>
</ul>
<a href="#" className="btn btn-outline">
view more
</a>
</div>
</div>
</div>
<div>
<div className="category-wrapper">
<div>
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/watch/cat2.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<h4>calculator watch</h4>
<ul className="category-link">
<li>
Shock-resistant watch
</li>
<li>
Skeleton watch
</li>
<li>
Slow watch
</li>
<li>
Solar-powered watch
</li>
</ul>
<a href="#" className="btn btn-outline">
view more
</a>
</div>
</div>
</div>
<div className="category-wrapper">
<div>
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/watch/cat3.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<h4>Antimagnetic watch</h4>
<ul className="category-link">
<li>
Watchmaking conglomerates
</li>
<li>
Breitling SA
</li>
<li>
Casio watches
</li>
<li>
Citizen Watch
</li>
</ul>
<a href="#" className="btn btn-outline">
view more
</a>
</div>
</div>
<div className="category-wrapper">
<div>
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/watch/cat2.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<h4>History of watches</h4>
<ul className="category-link">
<li>
Manufacture d'horlogerie
</li>
<li>
Mechanical watch
</li>
<li>
Microbrand watches
</li>
<li>
MIL-W-46374
</li>
</ul>
<a href="#" className="btn btn-outline">
view more
</a>
</div>
</div>
<div className="category-wrapper">
<div>
<div>
<img
src={`${process.env.PUBLIC_URL}/assets/images/watch/cat1.png`}
className="img-fluid blur-up lazyload bg-img"
alt=""
/>
</div>
<h4>watch models</h4>
<ul className="category-link">
<li>
d1 milano
</li>
<li>
damaskeening
</li>
<li>
diving watch
</li>
<li>
dollar watch
</li>
</ul>
<a href="#" className="btn btn-outline">
view more
</a>
</div>
</div>
</Slider>
</div>
</div>
</div>
</section>
{/*Category Six*/}
<section className="section-b-space">
<div className="container">
<div className="row background">
<div className="col">
<a href="#">
<div className="contain-bg">
<h4 data-hover="size 06">size 06</h4>
</div>
</a>
</div>
<div className="col">
<a href="#">
<div className="contain-bg">
<h4>size 07</h4>
</div>
</a>
</div>
<div className="col">
<a href="#">
<div className="contain-bg">
<h4>size 08</h4>
</div>
</a>
</div>
<div className="col">
<a href="#">
<div className="contain-bg">
<h4>size 09</h4>
</div>
</a>
</div>
<div className="col">
<a href="#">
<div className="contain-bg">
<h4>size 10</h4>
</div>
</a>
</div>
</div>
</div>
</section>
</div>
);
}
}
const mapStateToProps = store => {
return {
categories: store.category.categories
};
};
// export default connect(mapStateToProps, { getAllCategories })(ElementCategory);
export default connect(mapStateToProps, { getAllCategories })(ElementCategory);
I have isAdmin boolean property which I am checking user logged in as user or admin.
Backend is .net core 2.2, db - Postgre.
Everything works fine but after refresh I lose isAdmin value.
I have conditional show hide dropdown which is available only for admin roles.
How don't lose data after refreshing?
P.S. How to add logic also to my guard for isAdmin property?
My component looks like:
model: any = {};
constructor(public authService: AuthService, private alertify: AlertifyService, private router:
Router) { }
ngOnInit() {
}
login() {
this.authService.login(this.model).subscribe(next => {
this.model.isAdmin = true;
this.alertify.success('Logged in as Admin')
}, error => {
this.alertify.error(error)
}, () => {
this.router.navigate(['/projects'])
})
}
loginAsUser() {
this.authService.loginAsUser(this.model).subscribe(next => {
this.model.isAdmin = false;
this.alertify.success('Logged in as User')
}, error => {
this.alertify.error(error)
}, () => {
this.router.navigate(['/home'])
})
}
loggedIn() {
return this.authService.loggedIn();
}
logout() {
localStorage.removeItem('token');
this.alertify.message('logged out');
this.router.navigate(['/home'])
}
My html looks like:
<nav class="navbar navbar-expand-md navbar-light fixed-top bg-light">
<div class="container">
<a class="navbar-brand" [routerLink]="['/home']">
<img [src]="iteraLogo" alt="Itera">
</a>
<div *ngIf="loggedIn()" class="dropdown" dropdown [hidden]="!model.isAdmin">
<a class="dropdown-toggle" dropdownToggle>
<strong class="text-primary">Admin Panel</strong>
</a>
<div class="dropdown-menu mt-4" *dropdownMenu>
<ul class="navbar-nav">
<li class="nav-item" routerLinkActive="router-link-active">
<a class="nav-link" [routerLink]="['/projects']">Projects</a>
</li>
<li class="nav-item" routerLinkActive="router-link-active">
<a class="nav-link" [routerLink]="['/hypervisors']">Hypervisors</a>
</li>
<li class="nav-item" routerLinkActive="router-link-active">
<a class="nav-link" [routerLink]="['/management']">Management</a>
</li>
<li class="nav-item" routerLinkActive="router-link-active">
<a class="nav-link" [routerLink]="['/users']">Users</a>
</li>
<li class="nav-item" routerLinkActive="router-link-active">
<a class="nav-link" [routerLink]="['/user-projects']">Users Projects</a>
</li>
</ul>
</div>
</div>
<ul class="navbar-nav mr-auto">
<li class="nav-item" routerLinkActive="router-link-active">
<a class="nav-link" [routerLink]="['/test']">About</a>
</li>
</ul>
<div *ngIf="loggedIn()" class="dropdown" dropdown>
<a class="dropdown-toggle" dropdownToggle>
Welcome <strong>{{ authService.decodedToken?.unique_name | titlecase }}</strong>
</a>
<div class="dropdown-menu mt-3" *dropdownMenu>
<a class="dropdown-item text-primary" [routerLink]="['/projects/',
authService.decodedToken?.nameid ]"><i class="fa fa-archive"> My Projects</i></a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger" (click)="logout()"><i class="fa fa-sign-
out"> Logout</i></a>
</div>
</div>
<form *ngIf="!loggedIn()" #loginForm="ngForm" class="form-inline my-2 my-lg-0">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<i class="fa fa-user-circle-o text-primary" aria-hidden="true"></i>
</div>
</div>
<input class="form-control" placeholder="Username" name="username" required
[(ngModel)]="model.username" />
</div>
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<i class="fa fa-unlock text-danger" aria-hidden="true"></i>
</div>
</div>
<input class="form-control" placeholder="Password" name="password" required type="password"
[(ngModel)]="model.password" />
</div>
<div>
<button [disabled]="!loginForm.valid" type="submit" (click)="loginAsUser()" class="btn btn-primary my-2 my-sm-0">
<i class="fa fa-user-circle" aria-hidden="true"></i> User
</button>
<button [disabled]="!loginForm.valid" type="submit" (click)="login()" class="btn btn-success
my-2 my-sm-0">
<i class="fa fa-user-secret" aria-hidden="true"></i> Admin
</button>
</div>
</form>
</div>
</nav>
My guard looks like:
canActivate(): boolean {
if(this.authService.loggedIn()) {
return true
}
this.alertify.error('You have no access to see this page!!!');
this.router.navigate(['/home']);
return false;
}
When you refresh the page it does not persist variable values, you need to store either in local storage or cookie.
A simple way to solve this is to use this lib:
https://www.npmjs.com/package/ng2-cookies
To install this library, run:
npm install ng2-cookies
Component
import { Cookie } from 'ng2-cookies/ng2-cookies';
ngOnInit() {
this.model.isAdmin = Cookie.get('isAdmin');
}
login() {
this.authService.login(this.model).subscribe(next => {
Cookie.set('isAdmin', 'true');
this.alertify.success('Logged in as Admin')
}, error => {
this.alertify.error(error)
}, () => {
this.router.navigate(['/projects'])
})
}
you can use ngx-cookie-service also
https://www.npmjs.com/package/ngx-cookie-service
You will have to store your Auth_Token in localhost/indexDB/SessionStorage and then inside your route guard check if that token is valid or not.
This way your app will not require authentication until your token is valid.
Use this npm module to achieve this : enter link description here
You have to make the auth service API call with ngoninit of the components and get the isAdmin flag. That way when u refresh everytime ngOnInit will get involved and u ll get that flag.
ngOnInit(){ this.authService.login(this.model).subscribe(next => {
this.model.isAdmin = true;
});
}
Set a variable in localStorage upon successful login, like
isloggedIn(authUser) {
return this.httpClient.post<any>(`${this.apiUrl}/api/users/login`, {user: authUser})
.do(res => this.setSession(res.user))
.shareReplay();
}
private setSession = (authResult) => {
localStorage.setItem('TOKEN', authResult.token);
localStorage.setItem('loggedUser', 'y');
this._router.navigate(['dashboard'])
};
Next time when you enter any component, check
if(!localStorage.getItem('loggedUser')){
this._router.navigate(['login']);
return false;
}
return true;
this will authenticate user without calling API again. just get key from LocalStorage.