Link cannot appear as a descendant of a link - javascript

A React.js app gives the warning
Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a>.
See Element > a > ... > a.
What does that mean? How can you prevent it? Are nested links illegal in HTML, HTML5 or React.js?

That means:
<a href="1">
</a>
Is invalid HTML. Browsers will recognize this and turn this into:
React warns you about this because the browser's fix will make the actual DOM different from the virtual DOM, leading to strange bugs when React goes to update stuff.
Heed React's warning and don't nest <a> tags.

I was also getting this warning for my Navigation,
I was using react-bootstrap to render React Router Link in NavItem.
Warning: validateDOMNesting `<a>` cannot appear as a descendant of `<a>`.
in a (created by Link)
Fix: Add componentClass attribute to render NavItem as <span> tag instead of <a> tag (You can use any other tag name here)
My code was
<Nav pullRight>
<NavItem>
<Link to="/responses">Responses</Link>
</NavItem>
</nav>
After fix
<Nav pullRight>
<NavItem componentClass='span'>
<Link to="/responses">Responses</Link>
</NavItem>
</nav>
This also messes up styling, to fix it add the following CSS
.navbar-nav span[role=button] {
padding: 15px;
display: inline-block;
line-height: 20px;
}

What does that mean?
It means that:
<a href="http://example.com">
<span>
<a href="http://example.net">
...
</a>
</span>
</a>
and
<a href="http://example.com">
<a href="http://example.net">
...
</a>
</a>
and similar constructs are not allowed. They don't make sense anyway.
How can you prevent it?
This isn't something that happens without someone writing code that tries to do it.
You need to find that code and change it.
Are nested links illegal in HTML, HTML5 or React.js?
They are illegal in all versions of HTML. The first DTD for HTML was published as part of HTML 2. It says:
<!ELEMENT A - - %A.content -(A)>
The last part describes what content is allowed inside the element. The -(A) part of that means "Except for A elements".
HTML 5 is just the 2014 update to HTML. HTML 5.1 came out last year (2016). HTML 5.2 is under development.
React.js is a JavaScript library that generates an HTML DOM. The results are still (sort of) HTML.

You can use a function instead of a tag:
import React from 'react';
import PropTypes from 'prop-types';
import { Link, withRouter } from 'react-router';
class SomeClass extends React.Component {
constructor(props, context) {
super(props, context);
this.redirect = this.redirect.bind(this);
}
redirect(target) {
this.context.router.push("/path");
}
render() {
return (
<div>
<p onClick={this.redirect}>Link</p>
</div>
);
}
}
SomeClass.contextTypes = {
router: PropTypes.object
};
export default SomeClass;
Be careful to use a context.
I'm using a context for routing only.
class SomeClass extends React.Component {
constructor(props, context) {
super(props, context);
this.redirect = this.redirect.bind(this);
}
}

Related

Lit-Html js-api does not replace icon-elements

I have installed lit as well as fontawesome as npm packages.
I also use the Vaadin Router package for some routing.
Now I want to insert some icons on my own elements.
While the icons are replaced correctely in the index html they are not in all the seperate elements.
For replacing the icons I use the dom.i2svg method of the js-api from fontawesome. If I understood correctly this should also be used and activated automatically by using the npm package of frontawesome.
The files of interest are the following:
index.html
<body>
<navigation-element></navigation-element>
<i class="fab fa-linkedin"></i>
<main>
<div id="outlet"></div>
</main>
<footer-element></footer-element>
<script src="./vendor/webcomponents-loader.js"></script>
</body>
index.js
import { dom, library } from '#fortawesome/fontawesome-svg-core'
import { fas } from '#fortawesome/free-solid-svg-icons'
import { fab } from '#fortawesome/free-brands-svg-icons'
const outlet = document.getElementById('outlet');
const router = new Router(outlet);
router.setRoutes([
{ path: '/', component: 'my-element' },
]);
library.add(fas, fab);
dom.i2svg();
my-element.js
import { dom } from '#fortawesome/fontawesome-svg-core';
class MyElement extends LitElement {
static styles = css `
:host {
display: block;
}
`;
connectedCallback() {
super.connectedCallback();
dom.i2svg(); // does not work in here
}
render() {
return html `
<i class="fas fa-kiwi-bird"></i>
<i class="fab fa-linkedin"></i>
<div>
<p> text </p>
<p> text </p>
</div>
`;
}
}
customElements.define('my-element', MyElement);
In the index.js I added my icons to the library and in the index.html the the i element is correctly replaced. But for the my-element nothing happens. The library is filled with the icons but no elements are replaced. I tried to call the dom.i2svg() and/or dom.watch(params) inside the connecedCallback function aswell as in the constructor.
I also tried various other things/functions like importing css.
Someone else had a similar issue which could be solved on github, but this solution does not work for me.
Has someone an idea why? What did I wrong, is it maybe the router or is the api just broken?
Your component extends from LitElement which means it has a shadow DOM that shields its internal HTML from external CSS. You can:
Extend from View instead of LitElement to avoid using shadow DOM (if your project is based on Vaadin's Hilla starter)
Override createRenderRoot() { return this; } in your LitElement to not use shadow DOM. This will disable shadow DOM for whole component.
Include fontawesome styles in your component template with <style>
Use slot for content intended to be in light DOM. This is better approach when you want to partially protect the implementation, e.g. your component is a layout with dynamic content.

passing props as classNames in next.js

I am trying to have the header of each of my app's pages change color based on the current page. How I am trying to achieve this:
<Header className="headerBitcoin"></Header>
What I want is the be able to have the header component present on all 4 pages, and then just change the className to another prop to change the background but not anything else.
And the header component itself
import styles from "../../styles/Home.module.css";
export default function Header(props) {
return (
<div >
<div className={props.className}>aaaaa</div>
<div className={styles.row}>
<div className={styles.tab}>a</div>
<div className={styles.tab}>a</div>
<div className={styles.tab}>a</div>
<div className={styles.tab}>a</div>
</div>{" "}
</div>
);
}
At the moment the styles for the tabs and row are working but the header is not getting its style applied.
I checked the console and found the header is getting the className headerBitcoin passed to it, however the row beneath it has the className of "Home_row__88lPM"
This is my first time working with next.js, and I know I am doing something wrong because this works in React. Any help would be appreciated.
don't do this:
<div className={props.className}>aaaaa</div>
try this instead:
<div className={styles[props.className]}>aaaaa</div>
I think this should works
I assume it's not being applied because you have the headerBitcoin styles defined in your CSS module.
If you want to apply a class that way (className="headerBitcoin"), you need to define the class in your global CSS instead.
If you meant to use the headerBitcoin defined in Home.module.css, then you'll want to change the className to use the scoped styles.
import styles from "../../styles/Home.module.css";
export default function Header(props) {
return (
<div >
<div className={styles[props.className]}>aaaaa</div>
// ...
</div>
);
}

Styling Data React

I am new to React and having some trouble here- I am trying to use Sass to style a component in React. For some reason it isn't working. I am getting an error that 'styles' is defined but never used I'm not sure why this is happening as my other components are working fine. Probably something to do with the function around the jsx? I am getting an error 'styles' is defined but never used Any help is greatly appreciated!
Covers.js
import React from 'react';
import { videos } from '../../data/videos.json';
import styles from './covers.module.sass';
export const Covers = () => (
<div className="cover-container">
{videos.map((data, key) => {
return (
<Cover
key={key}
cover={data.cover}
title={data.title}
subtitle={data.subtitle}
description={data.description}
/>
);
})}
</div>
);
const Cover = ({title, subtitle, description, cover}) => {
return (
<div>
<img src={cover} className="cover-image" />
<h1 className="cover-title">{title}</h1>
<h2 className="cover-subtitle">{subtitle}</h2>
<p className="cover-description">{description}</p>
</div>
);
};
export default Covers;
covers.module.sass
.cover-container
text-align: center
color: white
.cover-image
width: 200px
height: 200px
object-fit: cover
.cover-title
font-size: 7em
.cover-subtitle
font-size: 4em
.cover-description
font-size: 2em
you are using CSS Modules but not using properly. CSS Modules allows you to scope locally your css, given an unique identifier to the classes for given scope. This avoids name collisions, a problem that can occur given the global scope nature in CSS.
but for that work properly at your file after importing the style as you do:
import styles from './covers.module.sass';
you need to use that styles object imported at your className declaration, rather than passing a string name like you do. Since you are not applying styles anywhere you get this error warning. You should pass to className style with the corresponding desired class.
therefore, the correct way to apply styles would be:
<div>
<img src={cover} className={styles.cover-image} />
<h1 className={styles.cover-title}>{title}</h1>
<h2 className={styles.cover-subtitle}>{subtitle}</h2>
<p className={styles.cover-description}>{description}</p>
</div>
as you do that your error is fixed, your styles work as expected, and at the browser you'll have unique classes generated which ensures that will not face any class name collision.
You can not use className like this <img src={cover} className="cover-image" />
correct would be <img src={cover} className={styles.cover-image} />
or
you can import sass file like this import './covers.module.sass'; and use className like this <img src={cover} className="cover-image" />
ref: https://www.w3schools.com/react/react_sass.asp
just replace the
import styles from './covers.module.sass';
with
import './covers.module.sass';

How to Create Route Links with Svelte Routify

I am learning how to use Routify to wire up some routes and links. I need help.
At the Routify docs it says:
"Links can be handled in an array of different ways. This might be the easiest way".
via: https://routify.dev/guide/navigation
It then gives this example:
<script>
import {isActive, url} from '#sveltech/routify'
const links =
[
['./index', 'Home'], //add index if you don't want siblings to be considered children
['./blog', 'Blog'],
['./about', 'About'],
['./contact', 'Contact']
]
</script>
<style>
.active {font-weight: bold}
</style>
<ul>
{#each links as [path, name]}
<!-- Svelte magic. If isActive is true, the "active" class is applied. -->
<li class:active={$isActive(path)}>
<a href={$url(path)}>
{name}
</a>
</li>
{/each}
</ul>
Unfortunately it doesn't give any instructions on how to wire this up to your app (if it does it's too complex for me to figure out).
I am using the Routify Template and I have copied the above code into the following directory:
pages/_components/Navigation.svelte
I then attempted to import it into pages/index.svelte as so:
pages/index.svelte
<script>
import Navigation from './_components/Navigation.svelte';
</script>
<div>
<Navigation/>
</div>
The links appear at the top of the page. When I click them the endpoint follows this pattern:
http://localhost:5000/index/home
http://localhost:5000/index/blog
http://localhost:5000/index/about
http://localhost:5000/index/contact
In my /pages directory I have pages that match the pages listed in the code such as:
/pages/Home.svelte
When I click the links my browser stays on the same page and does not embed the page associated with the link. Also, my browser developer tools say:
Uncaught Error: Route could not be found for "/index/home".
at urlToRoute (urlToRoute.js:15)
at updatePage (navigator.js:13)
at pushstate (navigator.js:60)
at History.history.<computed> [as pushState] (navigator.js:51)
What do I need to do so that I can see the respective pages and why is their an intermediate directory named "index"? How do I get rid of the word "index" in the url ?
UPDATE
Okay I have the navigation listed on all pages and semi-working.
I have my Navigation code (below) imported into pages/_layout.svelte.
The code is as follows:
pages/_layout.svelte
<script>
import Navigation from './_components/Navigation.svelte';
</script>
<!--TABS-->
<Navigation/>
pages/_components/Navigation.svelte
<script>
import {isActive, url} from '#sveltech/routify'
const links =
[
['./index', 'Home'], //add index if you don't want siblings to be considered children
['./form', 'Form']
]
</script>
<style>
.active {font-weight: bold}
</style>
<ul>
{#each links as [path, name]}
<!-- Svelte magic. If isActive is true, the "active" class is applied. -->
<li class:active={$isActive(path)}>
<a href={$url(path)}>
{name}
</a>
</li>
{/each}
</ul>
The navigation links appear but the problem now is that the content of the selected page does not appear.
In the image below, the form endpoint is being rendered. The form component has working HTML but the HTML does not appear on the page.
I am also getting a warning my my developer tools that says: " received an unexpected slot "default"
You shouldn't import your Navigation component into the index.svelte file but instead into a _layout.svelte file in the same directory.
They show the setup in the TV Shows App and although they don't show the above code imported in a Nav file like you're trying it will work if the component with the above code is imported in the _layout file since the url helper resolves paths relative to the page/layout in which it's used.
Importing it into the index file creates paths relative to the index file which is why you're seeing index in all routes.
UPDATE:
In response to needing to render the content of each page. This is done using a slot in the _layout file, in your example it would look like this:
<Navigation/>
<main>
<slot />
</main>

Ember JS: Call component action from another component

I am new to ember JS and after looking through the doc I still have no idea or don't understand how to achieve the following.
I have a navbar component that includes a menu button component
//components/nav-bar.hbs
<nav>
<LinkTo class="btn-1" #route="contact"><span>Contact</span></LinkTo>
<LinkTo class="btn-1" #route="about"><span>About</span></LinkTo>
<MenuButton>Menu</MenuButton>
</nav>
When this button is clicked I would like to toggle another component which is not a parent nor a child of menu-button component, nor of the nav-bar component
//components/nav-aside.hbs
<div class="core_page_cover">
</div>
<aside class="core_menu_aside">
</aside>
//components/nav-aside.js
import Component from "#glimmer/component";
import { action } from "#ember/object";
export default class NavAsideComponent extends Component {
#action
toggle() {
//toggle expanded | retracted class
}
}
I have no idea how to call the toggle action when clicking on the button and I guess I am missing something...
The components are encapsulated like so
// .--------------navbar.hbs-------------- //
// <nav>
// link link link link link link toggle-button //
// </nav>
// <MenuAside/>
Thanks a lot.
The solution here is to move the state and the action to the common ancestor.
It seems you have 3 components:
navbar
nav-bar
nav-aside
Where basically navbar encapsulates both nav-bar and nav-aside:
so you have basically this in your navbar:
<NavBar />
<NavAside />
and inside the nav-bar you have a button that should toggle if something inside nav-aside is shown.
Then the solution is to keep that information on the wrapping navbar component:
class NavbarComponent extends Component {
#tracked showSomethingInAside = false;
#action
toggleShowSomethingInAside() {
this.showSomethingInAside = !this.showSomethingInAside;
}
}
And use it like this to call your components in navbar:
<NavBar #toggleSomethingInAside={{this.toggleShowSomethingInAside}} />
<NavAside #showSomethingInAside={{this.showSomethingInAside}} />
Then inside nav-bar you can use the action:
<button type="button" {{on "click" #toggleSomethingInAside}}>
show something in aside
</button>
And inside nav-aside you can use the boolean:
{{#if #showSomethingInAside}}
This can be toggled by the button in the other component
{{/if}}
So you see the solution is to always keep the state in the right place. If multiple components share a state (because one changes it and the other reads it) that state belongs in neither component but a common ancestor component.

Categories