I'm tryin got use an anchor tag in Next.js
I don't get any console errors when I set it up and click the link, but the page does not jump to the id tag.
This issue on github suggests that people need to figure out a lot of custom code to use anchors. That can't be right.
I have:
const links = [
{ label: 'Solutions', href: '#solutions', id: 'solutions' },
]
<NavLink.Desktop key={index} href={link.href} id={link.id}>
{link.label}
</NavLink.Desktop>
I get no errors, but the page does not jump to the label that has an id of 'solutions'.
Does anyone know how to solve this, or where to go for ideas on how - it can't be intented that complex custom code is required to use an anchor tag?
Chakra UI has a Link component
<Link href='https://chakra-ui.com' isExternal>
Chakra Design system <ExternalLinkIcon mx='2px' />
</Link>
If you use the regular anchor tags
<Link href="#anchor_one">Menu one</Link>
<Link href="#anchor_two">Menu two</Link>
Then you can add the id for the anchors to the sections you want to navigate into
<div id="anchor_one" />
<div id="anchor_two" />
This can be either pages or components.
I hope this helped a little bit.
As said by #juliomalves in the comments, you have to specify the id attribute on the element in which you wish to navigate to. Not on the anchor tag.
The id for the anchor should be set on the element you want to link to, not on the link itself.
The below code works for me in Next.js -
export default function Home() {
return (
<div>
Click
<section
style={{ marginTop: "1000px", marginBottom: "1000px" }}
id="section"
>
<h1>Test</h1>
</section>
</div>
);
}
Your code should look like this -
const links = [{ label: 'Solutions', href: '#solutions', id: 'solutions' }]
<NavLink.Desktop
key={index}
href={link.href}
// id={link.id} - This is wrong, as you're referring to the same element
>
{link.label}
</NavLink.Desktop>
// Rather set the id attribute in a separate div/section element
<div id={link.id}>
<h2>Solutions</h2>
</div>
maybe try
const links = [
{ label: 'Solutions', href: '#solutions', id: 'solutions' },
]
<NavLink.Desktop key={index} href={links[0].href} id={links[0].id}>
{link.label}
</NavLink.Desktop>
since you only have 1 element in the links array, if you have multiple just map through the array
It is possible to scroll to anchor programatically using Router.push:
import { useRouter } from 'next/router'
const Foo = () => {
const { push } = useRouter()
const handleClick = () => {
push("#blah")
}
return (
<div>
<button onClick={handleClick}>Scroll</button>
<div>Foo</div>
<div>Bar</div>
<div id="blah">Blah</div>
</div>
)
}
Next.js recognises that you are passing something that is not a link to a new page and will concat it (in the example #blah) to the end of the URL.
Have a read about Link from next/link its a built in feature.
https://nextjs.org/docs/api-reference/next/link
https://github.com/vercel/next.js/blob/canary/examples/hello-world/pages/index.js#L7
Related
I'm currently using Angular for a few weeks for a small project and I wanted to add an anchor in my app.
So normally in order to add an anchor without using any framework, you'd create a block with an ID
<div id="top"> then you'd add an anchor tag <a href="#"> with the href attribute that would be equal to the ID of the block of the page we want to redirect the to.
ex:
<div id="top">...</div>
...
When we click on the link, it scrolls up to the page* to the block we defined the ID with.
*if we add in the CSS of the html or body tag scroll-behavior: smooth;
The issue is that when I add that inside my Angular template, it redirects me to the URL with the name of the ID on the href attribute!
If I take the previous example, here's what would happen:
localhost:4200/login → (click to the link) → localhost:4200/#top
Strangely it treats it as if it was a router link attribute
So I'm wondering how we could add an anchor in Angular
So I found a solution! (thanks to #Benjamin Looi for giving me the link to a relevant post)
So actually Angular has an issue with anchor tags when we want the user to another part of the same page
The solution is to use routerOptions of type extra options in the appRoutingModule and add in some options, here's the code:
const routes: Routes = [...];
const routerOptions: ExtraOptions = {
useHash: false,
anchorScrolling: 'enabled',
onSameUrlNavigation: 'reload' //Must have if you want to be able to use the anchor more than once
};
#NgModule({
imports: [RouterModule.forRoot(routes, routerOptions)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Then in the template, we have to add the anchor of the block we want to redirect the user in the attributes fragment with the name of the ID and routerLink with the page you're in (otherwise it will redirect to "/")
ex:
<a routerLink="/login" fragment="top"></a>
Now it will successfully redirect to the top of the page!
May be this might help
HTML code :
<button (click)="scrollToElement(target)"></button>
<div #target>Your target</div>
Ts code :
scrollToElement($element): void {
console.log($element);
$element.scrollIntoView({behavior: "smooth", block: "start", inline: "nearest"});
}
got it from this
Using HTML anchor link #id in Angular 6
Updated Question for more Clarity:
Need to display some texts and links as innerHTML(data from service/DB) in the Angular HTML and when user clicks, it should go to Typescript and programmatically navigates by router.navigate
Also, How to add DomSanitizer from #ViewChild/ElementRef
Added all example in below code
Here is the updated stackblitz code
As shown in screenshot from angular.io some texts and some links
Sorry, I didn't realize you answered my comment. Angular routing is not secondary, if you don't use Angular modules you'll end up with just an HTML/CSS/Typescript application. you need at least the RouterModule for Angular to be able to use routing and hence, do what it's supposed to with the DOM.
First:
You are not importing RouterModule
solution:
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot([]) // this one
]
Second:
You can't bind Angular events through innerHTML property
fix:
Make use of #ViewChild directive to change your innerHTML property and manually bind to the click event, so change in your app.component.html from
<div id="box" [innerHTML]="shouldbedivcontent" ></div>
to
<div #box id="box"></div>
Now, in your app.component.ts, add a property to hold a reference to that "box" element so you can later make some changes to the dom with it:
#ViewChild('box') container: ElementRef;
Implement AfterViewInit, that hook is where you will be able to actually handle your container, if you try using it for example in OnInit you'd get undefined because that component's html is not in the dom yet.
export class AppComponent implements AfterViewInit {
and
ngAfterViewInit() {
this.container.nativeElement.innerHTML = this.shouldbedivcontent;
this.container.nativeElement.addEventListener('click',
() => this.goto('bar')
);
}
change shouldbedivcontent property from:
'1) this is a click
<a (click)="goto("bar")">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto("bar")">Click</a><br>
3)This only works with href
bar and test'
to
'1) this is a click
<a id="link_1">Click</a><br>
2)this is with routerlink
<a [routerLink]="" (click)="goto(\'bar\')">Click</a><br>
3)This only works with href
bar and test'
And even so you'd still not get the default anchor style unless you apply some styling yourself.
Third
You are not HTML sanitizing, which could be dangerous. read more here
MY SUGGESTION:
Seems like a lot to do for you and a lot to read for someone else working alongside you for something you could easily do like in the example below!
Move your html to your app.component.html:
<div id="box">
1) this is a click
<a (click)="goto('bar')">Click</a><br>
2)this is with routerlink
<a routerLink="" (click)="goto('bar')">Click</a><br>
3)This only works with href
bar and test
</div>
<p>Below is actual content</p>
You'll notice that everything works now, except the anchor without routerLink or href, because that's not a link.
EDIT:
Looking at the new stackblitz, i suggest a change of approach, binding to innerHTML is ok when working with plain text or even some simple html but not a great choice to bind events or routing logic.
Angular's Renderer2 provides with a bunch of methods to dyncamically add elements to the DOM. With that on the table, you just need a little effort to take that simple html you get from your backend and turn it into something like (paste this property in your code to test it along the rest of the code provided below):
public jsonHTML = [
{
tagName: '',
text: 'some text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'bar',
attributes: {
value: 'bar' // goto parameter
}
},
{
tagName: '',
text: ' some more text with click ',
attributes: {
}
},
{
tagName: 'a',
text: 'foo',
attributes: {
value: 'foo' // goto parameter
}
}
]
Once you have it, it's way easier to create all of those elements dynamically:
this is for the code in your Q1:
Inject Renderer2 with private r2: Renderer2
And replace the Q1 related code in AfterViewInit hook to:
const parent = this.r2.createElement('div'); // container div to our stuff
this.jsonHTML.forEach((element) => {
const attributes = Object.keys(element.attributes);
const el = element.tagName && this.r2.createElement(element.tagName);
const text = this.r2.createText(element.text);
if (!el) { // when there's no tag to create we just create text directly into the div.
this.r2.appendChild(
parent,
text
);
} else { // otherwise we create it inside <a></a>
this.r2.appendChild(
el,
text
);
this.r2.appendChild(
parent,
el
);
}
if (attributes.length > 0) {
attributes.forEach((name) => {
if (el) {
this.r2.setAttribute(el, name, element.attributes[name]); // just the value attribute for now
if (name === 'value') {
this.r2.listen(el, 'click', () => {
this.goto(element.attributes[name]); // event binding with property "value" as parameter to navigate to
})
}
} else {
throw new Error('no html tag specified as element...');
}
})
}
})
this.r2.appendChild(this.container.nativeElement, parent); // div added to the DOM
No html sanitizer needed and no need to use routerLink either just inject Router and navigate to the route you want! Make improvements to the code t make it fit your needs, it should be at least a good starting point
Good Luck!
You have a css problem.
looks like a link
<a [routerLink]="something"></a> looks like a link, because if you inspect the HTML it actually gets an href property added because of routerLink
<a (click)="goTo()"></a> does NOT look like a link, because there is no href
Chrome and Safari default user agents css will not style <a> without an href (haven't confirmed Firefox but I'm sure its likely). Same thing for frameworks like bootstrap.
Updated stackblitz with CSS moved to global, not app.css
https://stackblitz.com/edit/angular-ivy-kkgmkc?embed=1&file=src/styles.css
This will style all links as the default blue, or -webkit-link if that browser supports it. It should be in your global.css file if you want it to work through the whole app.
a {
color: rgb(0, 0, 238);
color: -webkit-link;
cursor: pointer;
text-decoration: underline;
}
this works perfectly for me :D
#Directive({
selector: "[linkify]",
})
// * Apply Angular Routing behavior, PreventDefault behavior
export class CustomLinkDirective {
#Input()
appStyle: boolean = true;
constructor(
private router: Router,
private ref: ElementRef,
#Inject(PLATFORM_ID) private platformId: Object
) {}
#HostListener("click", ["$event"])
onClick(e: any) {
e.preventDefault();
const href = e.target.getAttribute("href");
href && this.router.navigate([href]);
}
ngAfterViewInit() {
if (isPlatformBrowser(this.platformId)) {
this.ref.nativeElement.querySelectorAll("a").forEach((a: HTMLElement) => {
const href = a.getAttribute("href");
href &&
this.appStyle &&
a.classList.add("text-indigo-600", "hover:text-indigo-500");
});
}
}
}
HOW I USE IT
<p linkify
class="mt-3 text-lg text-gray-500 include-link"
[innerHtml]="apiSectionText"
></p>
result
I have a small set of icons i want to call as a custom image prop depending on what type of item the component is. Code looks like this:
Vue.component('otherArticles', {
template: `
<!-- Component -->
<li>
<img :src="icon.text && icon.video" alt="icon">
<a :href="url">{{ Title }}</a>
</li>
`,
props: {
title: String,
url: String,
icon: [
{
text: "/resources/img/icons/text-icon.svg",
video: "/resources/img/icons/video-icon.svg"
}
]
}
});
Ideally in my html I would like to call them like this:
<!--Component with text icon-->
<other-articles
icon='text' <!-- how i'd like to call the text icon as img src -->
url="."
title="Text article title">
</other-articles>
<!--Component with video icon-->
<other-articles
icon='video' <!-- how i'd like to call the video icon as img src -->
url="."
title="Video article title">
</other-articles>
The img src binding is incorrect I know, i'm using it as an example of how i'm thinking it should be done, but I'm looking for any and all recommendations on how to do this correctly so I can call it the html as the example shows.
I only have these two icons and the src location for each may change but i would like to call it the same way even if i have to update the src location for either one in the future, keeping the html calls the same or similar. Any help would be appreciated. Thanks
First start by declaring your icon list as the following in your data function:
data() {
return {
iconList: {
text: '/resources/text.png',
video: '/resource/video.png',
}
};
}
Make sure to remove the list and rename the object, as you cannot have a prop and an entry in data with the same name. Then add your definition for icon to your props section as the following:
props: {
icon: {
type: String,
required: true,
},
},
This tells Vue to typecheck the prop as a string, and warn when it's not present or not a string.
Now you need to update your template function to use this new prop as an key to lookup the related icon:
template: `
<img :src="iconList[icon]"/>
`,
Now you can use your component as <comp icon="video"/>
I used tooltip form react-tooltip and I want to inside tooltip put some HTML tags. How to manage this? I don't find any information in react-tooltip site.
For now I create Tooltip:
const TooltipConst = props => {
if (props.tooltip && props.id) {
const tooltip = <Tooltip id={props.id + 'Tooltip'}>
<div> render(){props.tooltip} </div>
</Tooltip>;
return (
<OverlayTrigger
overlay={tooltip}
placement="top"
delayShow={500}
delayHide={1000}
>
{props.children}
</OverlayTrigger>
);
}
return <div>{props.children}</div>;
};
And when pass as tooltip some string with HTML they not rendered. Any advice?
I try also put as object, for example <span><p>some text</p> Some text </span>, but it return not text but something like Object[] as tooltip.
If you want to add html in ReactTooltip, like html button or other html tags in it. And wants to show on click.
<div id={row.index} className="text-center">
<a data-tip={'dummystring'} data-event={'click focus'}
data-for={'tooltip'}>Show tooltip</a>
<ReactTooltip id={'tooltip'} effect="solid"
clickable={true} place="right"
getContent={function() {
return (
<div>
<span>Some text</span>
<Button
onClick={()=>alert('clicked')}>
Click Me </Button>
</div>
)
}}/>
</div>
const tooltip = (<Tooltip id={props.id + 'Tooltip'}>
<div> render(){props.tooltip} </div>
</Tooltip>);
this is by their officiel documentation
also if you want to render html through props you should use dangerouslySetHTML => see React's documentation
You can use react-tooltip library.
Pass a prop html={true} to <ReactTooltip /> as <ReactTooltip html={true} /> for more information refer this link
This is an old question but I had a look into the documentation and they now have a data-html prop to detect if you want render html markup, something like this:
<ToolTipData data-tip={text} data-html={text.indexOf('</') > -1}>
{children}
</ToolTipData>
It's not super obvious from the docs but even if you are wanting HTML to be rendered inside the tooltip, it still needs to be in a string (wrap your HTML in backticks). Setting the html prop on the tooltip under the hood sets dangerouslySetInnerHtml on the string you pass in.
const inner = `<p>I'm html in a string</p><p>Same</p>`
and your tooltip:
<ReactTooltip
html={true}
id={"tooltip"}
place="right"
type="dark"
effect="solid"
>
{inner}
</ReactTooltip>
Say I have the following code in my database (user input):
<html>
<title>Test</title>
<body>testing website</body>
</html>
And I fetched it correctly from my database using ReactJS. How can I display this in say: 'localhost:3000/play'? I don't want it to be rendered as raw data just like the code but I want it to actually render the html body as a website. (Title set to Test, and displays a small text: testing website). How can I do that in ReactJS? I already have /play configured and I just want to know how to display it there in the index.js file. I tried something like <div dangerouslySetInnerHTML={template} /> but it didn't work.
So, I fixed it with the following:
In a separate function:
db.getHTMLBody(key).then(snapshot => {
this.setState({ body: snapshot.val() })
})
then in the render:
<div dangerouslySetInnerHTML={{ __html: this.state.body }} />
Please try :
const innerHtml = { __html: escape(snapshot.val()) }
return(<div dangerouslySetInnerHTML={innerHtml} />)