How do I do a paragraph ("p") tag in Arbre? - arbre

I can do a div tag like this:
2.1.0 :014 > Arbre::Context.new { div "foo" }
=> <div>foo</div>
And a span tag like this:
2.1.0 :017 > Arbre::Context.new { span "foo" }
=> <span>foo</span>
But this convention doesn't work for the p (paragraph) tag:
2.1.0 :020 > Arbre::Context.new { p "foo" }
"foo"
=>
Obviously this doesn't work because p is already used in Ruby.
How do I do a paragraph tag in Arbre?

In Arbre, the paragraph function is para:
2.1.0 :021 > Arbre::Context.new { para "foo" }
=> <p>foo</p>
I was not able to find this in any docs. I just got lucky.

Related

how to run document.getelementbyclassname() while using node js

Im learning node js and i am trying to make a mega menu appear when hovering over a link in the header. I understand that 'document.getElementByClassName' won't work because of ssr but I do not know the solution to my problem.
Ive tried to use the dynamic import from the docs but dont quite get how to implement it into my code. Below is a picture.
image
If you are using the current version of nextjs, you don't have to worry about SSR when your page component doesn't load anything dynamically. Just leave the _app.js and _document.js to its default since you are a beginner.
create a folder called components outside of the pages folder. Inside that folder create a file named Base.js and paste this code
import React, {
useState
} from "react";
const Menu = () => {
const [hide, setHide] = useState(true);
const showMenu = (e) => {
e.preventDefault();
setHide(false);
};
const hideMenu = (e) => {
e.preventDefault();
setHide(true);
};
return ( <
div className = "container" >
<
div className = "dropdown"
onMouseEnter = {
showMenu
}
onMouseLeave = {
hideMenu
} >
Menu <
/div> <
ul className = "menu-area"
style = {
{
display: hide ? "none" : "block"
}
} >
<
li > Home < /li> <
li > Home < /li> <
li > Home < /li> <
li > Home < /li> < /
ul > <
/div>
);
};
const Base = ({
children
}) => {
return ( <
div >
<
Menu / > {
children
} <
/div>
);
};
export default Base;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Now you can import the Base component to every other component and wrap everything in those components inside the Base component like this -> <Base> /*your code */</Base>

Insert anchor tag in string variable

I am trying to insert an anchor tag in a string variable. The string will be shown on screen as a link, but it seems to ignore the anchor tag, and only including the value inside the tag.
example something turns to just 'something'
private generateAnchors(content: string) {
let contentToReturn = content;
if (content.indexOf('#') < 0) return contentToReturn;
else if (content.indexOf('#') > -1) {
let tag = content.substring(content.indexOf('#'), content.indexOf(' ', content.indexOf('#')));
let address = tag.substring(1, tag.length);
let anchor = '<a href="/home/user/"'+address+'>'+tag+'</a>';
console.log('found substr: '+tag, ' '+address+' '+anchor);
contentToReturn.replace(tag, anchor);
console.log('final string: '+contentToReturn);
}
return contentToReturn;
}
How is this done in Angular/TypeScript ?
Did you try to replace:
contentToReturn.replace(tag, anchor);
with:
contentToReturn = contentToReturn.replace(tag, anchor);
String.replace doesn't change your string since it's immutable, it just returns a new one with the replacement done.
Angular Templating
Apart from this little issue, you should consider using Angular's templating mechanics. You should read the documentation to learn this but I'll try to give you an example that fits your particular case.
You'd have one links.component.html file with:
{{tag}}
Or maybe a loop if you need several of them:
<div *ngFor="let link in links">
{{link.tag}}
</div>
And a corresponding links.component.ts with the corresponding elements:
import { Component } from "#angular/core";
#Component({
selector?: "app-links-component"
templateUrl: "links.component.html"
})
export class LinksComponent {
address: string;
tag: string;
constructor() {}
// ts logic to fill address/tag information
}
Or an array in the second case:
import { Component } from "#angular/core";
#Component({
selector?: "app-links-component"
templateUrl: "links.component.html"
})
export class LinksComponent {
links: Link[] = [];
constructor() {}
generateAnchors(address: string, tag: string) {
// You can bind this method to a form submit or something.
this.links.push(new Link(address, tag));
}
}
N.B.: Since this is all written by heart, there might be typos here and there...

Using Fragment to insert HTML rendered on the back end via dangerouslySetInnerHTML

I used to compile and insert JSX components via
<div key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
which wrapped my HTML into a <div>:
<div>my html from the HTML object</div>
Now react > 16.2.0 has support for Fragments and I wonder if I can use that somehow to avoid wrapping my HTML in a <div> each time I get data from the back end.
Running
<Fragment key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
will throw a warning
Warning: Invalid prop `dangerouslySetInnerHTML` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.
in React.Fragment
Is this supported yet at all? Is there another way to solve this?
Update
Created an issue in the react repo for it if you want to upvote it.
Short Answer
Not possible:
key is the only attribute that can be passed to Fragment. In the
future, we may add support for additional attributes, such as event
handlers.
https://reactjs.org/docs/fragments.html
You may want to chime in and suggest this as a future addition.
https://github.com/facebook/react/issues
In the Meantime
You may want to consider using an HTML parsing library like:
https://github.com/remarkablemark/html-react-parser
Check out this example to see how it will accomplish your goal:
http://remarkablemark.org/blog/2016/10/07/dangerously-set-innerhtml-alternative/
In Short
You'll be able to do this:
<>
{require('html-react-parser')(
'<em>foo</em>'
)}
</>
Update December 2020
This issue (also mentioned by OP) was closed on Oct 2, 2019. - However, stemming from the original issue, it seems a RawHTML component has entered the RFC process but has not reached production, and has no set timeline for when a working solution may be available.
That being said, I would now like to allude to a solution I currently use to get around this issue.
In my case, dangerouslySetInnerHTML was utilized to render plain HTML for a user to download; it was not ideal to have additional wrapper tags included in the output.
After reading around the web and StackOverflow, it seemed most solutions mentioned using an external library like html-react-parser.
For this use-case, html-react-parser would not suffice because it converts HTML strings to React element(s). Meaning, it would strip all HTML that wasn't standard JSX.
Solution:
The code below is the no library solution I opted to use:
//HTML that will be set using dangerouslySetInnerHTML
const html = `<div>This is a div</div>`
The wrapper div within the RawHtml component is purposely named "unwanteddiv".
//Component that will return our dangerouslySetInnerHTML
//Note that we are using "unwanteddiv" as a wrapper
const RawHtml = () => {
return (
<unwanteddiv key={[]}
dangerouslySetInnerHTML={{
__html: html,
}}
/>
);
};
For the purpose of this example, we will use renderToStaticMarkup.
const staticHtml = ReactDomServer.renderToStaticMarkup(
<RawHtml/>
);
The ParseStaticHtml function is where the magic happens, here you will see why we named the wrapper div "unwanteddiv".
//The ParseStaticHtml function will check the staticHtml
//If the staticHtml type is 'string'
//We will remove "<unwanteddiv/>" leaving us with only the desired output
const ParseStaticHtml = (html) => {
if (typeof html === 'string') {
return html.replace(/<unwanteddiv>/g, '').replace(/<\/unwanteddiv>/g, '');
} else {
return html;
}
};
Now, if we pass the staticHtml through the ParseStaticHtml function you will see the desired output without the additional wrapper div:
console.log(ParseStaticHtml(staticHtml));
Additionally, I have created a codesandbox example that shows this in action.
Notice, the console log will throw a warning: "The tag <unwanteddiv> is unrecognized in this browser..." - However, this is fine because we intentionally gave it a unique name so we can easily differentiate and target the wrapper with our replace method and essentially remove it before output.
Besides, receiving a mild scolding from a code linter is not as bad as adding more dependencies for something that should be more simply implemented.
i found a workaround
by using react's ref
import React, { FC, useEffect, useRef } from 'react'
interface RawHtmlProps {
html: string
}
const RawHtml: FC<RawHtmlProps> = ({ html }) => {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!ref.current) return
// make a js fragment element
const fragment = document.createDocumentFragment()
// move every child from our div to new fragment
while (ref.current.childNodes[0]) {
fragment.appendChild(ref.current.childNodes[0])
}
// and after all replace the div with fragment
ref.current.replaceWith(fragment)
}, [ref])
return <div ref={ref} dangerouslySetInnerHTML={{ __html: html }}></div>
}
export { RawHtml }
Here's a solution that works for <td> elements only:
type DangerousHtml = {__html:string}
function isHtml(x: any): x is DangerousHtml {
if(!x) return false;
if(typeof x !== 'object') return false;
const keys = Object.keys(x)
if(keys.length !== 1) return false;
return keys[0] === '__html'
}
const DangerousTD = forwardRef<HTMLTableCellElement,Override<React.ComponentPropsWithoutRef<'td'>,{children: ReactNode|DangerousHtml}>>(({children,...props}, ref) => {
if(isHtml(children)) {
return <td dangerouslySetInnerHTML={children} {...props} ref={ref}/>
}
return <td {...props} ref={ref}>{children}</td>
})
With a bit of work you can make this more generic, but that should give the general idea.
Usage:
<DangerousTD>{{__html: "<span>foo</span>"}}</DangerousTD>

MarkupBuilder don't escape element value

I am trying to generate HTML using MarkupBuilder and would like to know if I can turn of escaping values, see example below.
new MarkupBuilder().with {
setEscapeAttributes(false)
p('Hello <strong>World!</strong>')
p('Hello ') { strong('World!') }
}
The strong tag within the first p tag is escaped. The second strong tag within the second p tag is not escaped. How can I configure MarkupBuilder to not escape the strong tag in the first p tag?
<p>Hello <strong>World!</strong></p>
<p>Hello
<strong>World!</strong>
</p>
escapeAttributes works for attributes. You're not setting attributes
You need to use mkp.yieldUnescaped
new MarkupBuilder().with {
p { mkp.yieldUnescaped 'Hello <strong>World!</strong>' }
p('Hello ') { strong('World!') }
}

Remove null attributes and empty children from Node

I have a Node which I want to remove all null attributes and empty children from recursively before I write it to a file. What I have below works, but it seems like there should be a built in method for this. Have I missed something?
Node cleanNode(Node node) {
// Find null attributes
def attributesToRemove = []
for(e in node.attributes()) {
if(e.value == null) {
attributesToRemove.add(e.key)
}
}
// Remove null attributes
for(attribute in attributesToRemove) {
node.attributes().remove(attribute)
}
// Clean this node's children
for(child in node.children()) {
if(child instanceof Node) {
cleanNode(child)
}
}
// If node has no attributes, no children, and no text then discard it by setting it to null
if(!node.attributes() && !node.children() && !node.text()) {
node = null
}
node
}
There's not a build in method for doing this that I know of... You could get your code a little smaller (and recursively remove empty children) like so:
boolean cleanNode( Node node ) {
node.attributes().with { a ->
a.findAll { !it.value }.each { a.remove( it.key ) }
}
node.children().with { kids ->
kids.findAll { it instanceof Node ? !cleanNode( it ) : false }
.each { kids.remove( it ) }
}
node.attributes() || node.children() || node.text()
}
So given the xml:
def xml = '''<root>
| <head>
| <item>
| <woo/>
| </item>
| </head>
| <body att=''>
| <h1 name='' title='title'>
| woo
| </h1>
| </body>
| <tail>
| <item>hi</item>
| </tail>
|</root>'''.stripMargin()
We can parse it and clean it and print it out:
Node root = new XmlParser().parseText( xml )
cleanNode( root )
println XmlUtil.serialize( root )
which gives:
<?xml version="1.0" encoding="UTF-8"?><root>
<body>
<h1 title="title">woo</h1>
</body>
<tail>
<item>hi</item>
</tail>
</root>
As you can see, the whole <head> block has been cleaned as it contained no information.
If the xml contains namespaces the cleanNode method will result in namespace declarations sprayed onto every node.
To avoid this use new XmlParser(false, false), so the code becomes:
Node root = new XmlParser(false, false).parseText(xml)
cleanNode(root)
println XmlUtil.serialize(root)
This way the xml is nice and clean afterwards.
Many thanks to Tim for the original answer!

Resources