We run a few ASP.NET Core applications that do come with some pages. These are all "classic" multipage applications.
For our new portal we've decided to go with piral. While we add a couple of new modules we also want to use the existing applications.
How can we integrate a multipage application in piral or in a clientside (SPA) micro-frontend?
One example for a multipage app looks like (hosted on some address like https://myexample.com/home)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Privacy Policy - DotnetApp</title>
<link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" href="/">DotnetApp</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" href="/Home/Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
<h1>Privacy Policy</h1>
<p>Use this page to detail your site's privacy policy.</p>
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2020 - DotnetApp - Privacy
</div>
</footer>
<script src="/lib/jquery/dist/jquery.min.js"></script>
<script src="/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>
</body>
</html>
We don't need to have all scripts - most of them are just there due to the boilerplate. Actually we can also run without all of them.
Migration of an MPA to Piral can be done in multiple ways.
Create a wrapper pilet that exposes the different SSRed parts via iframes
Create a wrapper pilet that exposes the different SSRed parts via fragments which are dynamically obtained
Convert your Razor views into React parts and have a split between FE/BE; put the React parts in a pilet
I don't know how complicated you application is, but maybe (1) would be the easiest to get started (especially if its, e.g., just a single page).
Let's see how (2) may be implemented:
import * as React from 'react';
export function setup(app) {
const connect = app.createConnector(() => fetch('https://myexample.com/home').then(res => res.text()));
app.registerPage('/myexample', connect(({ data }) => {
const __html = data;
return <div dangerouslySetInnerHTML={{ __html }} />;
}));
}
Here, we lazy load content from https://myexample.com/home and display it in the component (which will be a page).
There are a couple of things you should watch out for:
In the best case detect the fetch (or send a special header) and only return a fragment instead of the full response
If stylesheets are needed either "fish" them out of the response or integrate them upfront (see next point)
The code above has no special SPA link handling. Thus all links are relative to the given page URL and they are making a full transition ...
JavaScript would not be loaded / working in the given solution
Now regarding a stylesheet you could do (among other things):
import './my-style.css';
import * as React from 'react';
export function setup(app) {
// ...
}
where my-style.css looks like:
#import url(https://myexample.com/css/site.css);
We could have more URLs etc. here, but I leave that out on purpose. Keep in mind that the previously shown way could introduce collisions. So either "download" the sheet upfront and prefix ("scope") it, or use it all in a shadow DOM solution (shadow DOM is always "free" of the parent DOM's style and needs to import its own stylesheets).
For the link handling what you can do: Use a layout effect, get all a elements via a selector, attach an event and cancel the original navigation. Use the history context instead.
In code this looks similar to:
import * as React from 'react';
import { useHistory } from 'react-router';
export function setup(app) {
const connect = app.createConnector(() => fetch('https://myexample.com/home').then(res => res.text()));
app.registerPage('/myexample', connect(({ data }) => {
const __html = data;
const container = React.useRef();
const history = useHistory();
React.useLayoutEffect(() => {
if (container.current) {
const anchors = container.current.querySelectorAll('a[href]');
Array.prototype.forEach.call(anchors, anchor => {
anchor.addEventListener('click', ev => {
ev.preventDefault();
history.push(anchor.getAttribute('href'));
});
});
}
}, []);
return <div dangerouslySetInnerHTML={{ __html }} ref={container} />;
}));
}
Related
I am new to working on the frontend. I'm learning how to use React and react-router-dom library. The program renders without a problem normally. The problem lies when I try to add a Link component from the React library. When I do this, the DOM is no longer displayed.
import React from "react"
import {Routes, Route, Link} from "react-router-dom"
import Mflix from "./components/mflix.js"
function App(){
return (
<div className="App">
<nav className="navbar navbar-expand navbar-dark bg-dark">
<a href="/mflix" className="navbar-brand"> {/*href links to "/mflix" route */}
Mflix Reviews
</a>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/mflix"} className="nav-link">
Movies
</Link>
</li>
</div>
</nav>
</div>
)
}
This SO forum has a similar problem, except they created a class for their app, and I'm trying to keep it as a function for now.
If I replace the code:
<Link to={"/mflix"} className="nav-link">
Movies
</Link>
with
<a href="/mflix"m className="nav-link">
Movies
</a>
The DOM renders fine, buT I want to use Link because it doesn't force my page to refresh every time.
Edit: I read that instead of passing a JS expression to the to <Link to{"/mflix"}/> I should pass a string <Link to"/mflix"/>. However, the documentation says that if the link uses a path name, then it should be passed to to= as an object, and not a string. IN any case, I tried the suggestion, but the code still returns a blank page.
Edit: This is what the web console has to say about the component:
he above error occurred in the <Link> component:
LinkWithRef#http://localhost:3000/static/js/bundle.js:37314:9
li
div
nav
div
App#http://localhost:3000/static/js/bundle.js:40:72
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
Whenever the DOM disappears, there is an issue in your code. I'm guessing it has to do with this "Mflix" import. Bring up the console and see what it says.
I have a static 2-page Next.js app that I want to integrate a user-based system with. I've decided to use Auth0 for authentication. My goal is to let a user see documents that they have saved on my app, similar to Grammarly, but I am not sure how the entire UI will conditionally change after the user has logged in.
For example, think about the NavBar, which is a component in my app named navbar.js.
import Link from "next/link";
export const siteTitle = "Title";
export const siteDescription = "Description";
export default function Navbar({ page, ...props }) {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-white">
<div className={page == "page1" ? "container-fluid" : "container"}>
{/* <!-- Toggle Open --> */}
{page == "Page1" || (
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarCollapse"
>
</button>
)}
{/* <!-- Collapse --> */}
<div className="collapse navbar-collapse" id="navbarCollapse">
{/* <!-- Toggle Close --> */}
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarCollapse"
>
</button>
{/* <!-- Buttons --> */}
{page == "Page1" ? (
""
) : (
<Link href="/page1">
<a className="navbar-btn">
Page 1
</a>
</Link>
)}
{page == "Link1" ? (
""
) : (
<Link href="https://link2">
<a className="secondary-navbar-btn">
Link 1
</a>
</Link>
)}
</div>
</div>
</nav>
);
}
Considering that the navbar will changing after login, (I want to have "documents" that the user can save directly and later access, meaning that a "My Documents" button must appear in the navbar after login) I am unsure of how the navbar.js file should now be written. Should I just created a new component that will be used after login; if so how would that look? Or should those extra buttons be added in my original navbar.js file, but only rendered after login? Are there existing react hooks that modify components depending on login status?
I would greatly appreciate any links to documentation or tutorials that are solving a problem similar to mine, since I have been unable to find these resources on my own.
Edit 1: If there is anything that I can add/take away/change on this post to make it better please let me know.
Edit 2: I have brain stormed 2 ways of updating the UI in general but I would like to hear some more experienced opinions on my strategies.
Option 1: Update individual components from within the same file after the user logs in. I still don't know what this would look like though.
Option 2: Direct the user to a new page with entirely different components. I won't be able to reuse my existing components and might be repeating code a bit. However I don't mind this option if it is the safer route.
I am open to literally any suggestions at all, just trying to get this thing working
The simplest way is option 2, Auth0 provides hooks that tell you whether there is a user logged in or not and you can use that to conditionally render components. I usually use ternary operators to do that within JSX.
I'd also suggest learning a state management API such as Redux or Context API and then conditionally rendering components based on whether the logged-in state is true or false.
Basically I'm trying to remake some simple web page that I have initially created with HTML and CSS to be working rather on React. I managed to redo the page to correctly display when it was moved into React, however I don't really understand why the navigation links that I have on top do not take me to the corresponding section on the same page anymore as well as why the external links to the project sites also stopped working.
Here is the project link code:
import React from "react";
export default function ProjectTile(props) {
return (
<div className="project-tile" id={props.id}>
<a href={props.href} target="_blank" id={props.link_id}>
<img
className="project_screenshot"
src={props.img_src}
alt={props.img_alt}
/>
<p className="project_name">
<span className="brackets"><</span> {props.title}{" "}
<span className="brackets">/></span>
</p>
</a>
</div>
);
}
All props are getting mapped and loaded from the array with corresponding data where each object looks like this:
{
id: "tribute_page",
link_id: "https://codepen.io/konstantinkrumin/full/PooYQbG",
img_src: "https://i.imgur.com/ynRuzOQ.png",
img_alt: "tribute_page_screenshot",
title: "Tribute Page"
}
The navigation links used are the following:
import React from "react";
export default function Navbar() {
return (
<nav id="navbar">
<a className="nav-link" href="#welcome-section">
About
</a>
<a className="nav-link" href="#projects">
Projects
</a>
<a className="nav-link" href="#contact">
Contact
</a>
</nav>
);
}
And each section they refer to have an id corresponding to the href indicated above.
Here if the link to this project on codesandbox
P.S. Everything used to work correctly when it was on HTML.
Also the contact links that seem to be set in similar way as project links are working.
Here are two things I think I found out:
In the ProjectTile.js file, replace href = {props.href} by href={props.link_id and now project opens in codepen.
About the jump link you have made in nav-bar, I think it's because of problem of codesandbox.
If you manage to make your url to https://op6gq.csb.app#projects instead of https://op6gq.csb.app/#projects. That's gonna work.
Or directly visiting https://op6gq.csb.app/#welcome-section jump link too works well.
It looks like there's no href prop. Sounds like what you want is something like
href={`#${props.id}`}
which would evaluate to href="#tribute_page" in this example.
You Have to try that your page url become:
https://op6gq.csb.app#welcome-section
Not:
https://op6gq.csb.app/#welcome-section
please attend to that / in address bar!
I am building a website using Gatsby and Bulma. In my Nav.js file, where I create a general format for the buttons at the top of the website, I have a few buttons using Bulma to which I would like to add icons inside. I went off the documentation for adding Bulma buttons with Font Awesome Icons: https://bulma.io/documentation/elements/button/. My code is exactly the same, other that the fact that I have my buttons wrapped in an <a> tag to link to other pages in my website. I have the included <script> file listed in documentation to have Font Awesome Icons available, and my code looks as such:
const Nav = () => {
return (
<div style={{ margin: `3rem auto`, maxWidth: 650, padding: `0 1rem` }}>
<nav>
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
<p class="buttons is-outlined is-centered">
<a href="/"><button class="button is-outlined"> <span class="icon">
<i class="fas fa-home"></i>
</span>
<span>Home</span>
</button></a>
<button class="button is-outlined">Projects</button>
<button class="button is-outlined">Experience</button>
</p>
</nav>
</div>
)
}
I'm not sure if I have the script located in the correct part of the file, and I've only tried to put an icon for my Home button which looks like this:
The gap to the left of the "Home" is where I'm guessing the icon should be. I would appreciate any help as to why the icon is not showing up or is showing up blank. Thank you!
I ran into this issue myself so posting here for anyone that is looking for the answer. There are a few ways to make it work, including using the icons as components with a library such as react-fontawesome. However if you're using Bulma then chances are that you specifically don't want to do that, instead you want to use the class names.
So first install the package:
npm i #fortawesome/fontawesome-free
Then in your index.js / app.js / any styling wrapper component you have:
import '#fortawesome/fontawesome-free/css/all.min.css'
Here is a Typescript example I have in front of me. This is a wrapper component that imports all my global styles for nested child components to use:
import React from 'react';
import 'bulma/css/bulma.css';
import '#fortawesome/fontawesome-free/css/all.min.css';
import NavMenu from '../nav-menu';
import Footer from '../footer';
import './layout.css';
const Layout: React.FC<{ light: boolean }> = ({ light, children }) => {
return (
<div className="layout-wrapper">
<NavMenu light={light} />
{children}
<Footer light={light} />
</div>
);
};
export default Layout;
With the help of a friend, what solved the issue was putting the <script> tag in the public/index.html file of the project, and then making an exact copy and naming it index.html and putting it in the static folder in the project. This way, each time a Gatsby server is ran, it will create a copy of the index.html file in the public repository with the Font Awesome Icon script included.
From Gatsby's official docs regarding Gatsby's Link component, it states that the Link component is used only for internal links, whereas for external links, one has to use the tag.
I'm building a Button component that has inbuilt props for links. The problem is right now I have to create 2 separate Button components for internal and external links due to the limitation.
My goal is to use one freeLink component that can be used as both internal and external links
I've tried creating a subcomponent (Button) for the button, but I'm unsure of the parent component (freeLink) which requires conditional rendering. The subcomponent is as of follows:
const Button = props => (
<button className={props.btnType}>
<span>{props.text}</span>
</button>
)
This is the visual logic to what I want to achieve:
For Internal links
<freeLink intLink="/about" btnType="btn-cta" text="Read about us">
</freeLink>
...which will render...
<Link to="/about">
<button className="btn-cta">
<span>Read about us</span>
</button>
</Link>
It is relatively similar for external links
<freeLink extLink="https://google.com" btnType="btn-cta" text="Visit Our Partner">
</freeLink>
...which will render...
<a href="https://google.com">
<button className="btn-cta">
<span>Visit Our Partner</span>
</button>
</a>
I'm quite new to Javascript, Gatsby and React so I'm unsure to how to apply a conditional rendering based on props applied.
Any advice, suggestion, or direction to how to code up the freeLink component is greatly appreciated.
P.S: I've seen Conditionally Use Gatsby Link in React Compoment but the chosen answer is too complicated for me to understand, and I don't have enough points to comment to ask for further elaboration.
You could try something simple like this:
const MyLink = (href, text, ...props) => {
if (href.startsWith("http") {
return <a href={href} {...props}>{text}</a>
} else {
return <Link href={href} {...props}>{text}</Link>
}
}
Your component could return different stuff based on weather you pass it a to or a href prop:
import { Link } from "gatsby"
const freeLink = props => {
if (props.to) return <Link {...props} />
return <a {...props} />
}`