I am using Markdown file to generate pages for gatby. In order to control the style of pictures, I use html syntax. However, the page generated by gatsby does not display the html part.
This is my markdown file:
---
......frontmatter......
---
......content......
<table>
<tr>
<td><img src="./images/2018/zotero/ZoteroWebDAV.png"></td>
<td><img src="./images/2018/zotero/ZoteroExts.png" width=100%></td>
</tr>
</table>
......content......
Everything else is rendered normally, however, neither the table nor the pictures in it are displayed. Here is my gatsby-config.js.
{
resolve: `gatsby-transformer-remark`,
options: {
excerpt_separator: `<!-- endexcerpt -->`,
plugins: [
// 'gatsby-remark-relative-images',
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 1200,
},
},
{
resolve: `gatsby-remark-image-attributes`,
options: {
dataAttributes: true
}
},
],
},
},
What can I do to make the html part in Markdown render normally?
You can use as well the built-in dangerouslySetInnerHtml property or any markdown parser like markdown-to-jsx.
Using the first approach, following Gatsby's guides:
import React from "react"
import { graphql } from "gatsby"
export default function Template({data}) {
const { markdownRemark } = data // data.markdownRemark holds your post data
const { frontmatter, html } = markdownRemark
return (
<div className="blog-post-container">
<div className="blog-post">
<h1>{frontmatter.title}</h1>
<h2>{frontmatter.date}</h2>
<div
className="blog-post-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
</div>
)
}
export const pageQuery = graphql`
query($id: String!) {
markdownRemark(id: { eq: $id }) {
html
frontmatter {
date(formatString: "MMMM DD, YYYY")
slug
title
}
}
}
`
Because you haven't shared your query I've used the one in the guide but tweak it as you wish. As you can see, everything that is in the end of the frontmatter is html:
<div
className="blog-post-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
Using the second approach, and following the previous query structure, the html should be rendered as:
import Markdown from 'markdown-to-jsx';
import React from 'react';
<Markdown>{html}</Markdown>
If there's any hindrance I'd say that the second approach is better because, as the dangerouslySetInnerHTML name suggests, you are potentially exposing your site to XSS (Cross-Site Scripting), while the second approach sanitizes the implementation.
Related
I'm working on a support site with strapi(backend) and gatsbyjs/react (frontend).
I am trying to retrieve from my admin strapi the text I have underlined and display it in my frontend but it is not retrieving the <u> tag correctly and is displaying the <u> tag instead of the underlined text.
Is it because of my react-markdown?
here is my webview:
when I look at the element inspector I see inverted commas before my tag which is why it doesn't work
and here is my code:
import React from "react";
import { graphql, PageProps } from "gatsby";
import ReactMarkdown from "react-markdown";
import { ArticleT } from "#faq/types";
import Layout from "#faq/components/Layout";
import * as s from "./Article.module.css";
import { toArticle, toCategory } from "#faq/utils/routes";
type ArticlePageProps = {
strapiArticle: ArticleT;
file: {
publicURL: string;
};
};
const Article: React.FC<PageProps<ArticlePageProps>> = ({
data: { strapiArticle },
}) => {
const { title, content, category, slug } = strapiArticle;
return (
<Layout
seo={{ pageTitle: title, pageUrl: toArticle(category.slug, slug) }}
breadcrumb={[
{ to: toCategory(category.slug), label: category.title },
{ label: title },
]}
>
<div className={s.wrapper}>
<h1 className={s.title}>{title}</h1>
<div className={s.author}>
<div className={s.texts}>
<div>
<span className={s.light}>Écrit par</span> Nicolas
</div>
</div>
</div>
<ReactMarkdown children={content}
className={s.content}
transformImageUri={uri => uri.startsWith("http") ? uri : `${process.env.GATSBY_IMAGE_BASE_URL}${uri}`} />
</div>
</Layout>
);
};
export default Article;
export const query = graphql`
query ArticleQuery($id: Int!) {
strapiArticle(strapiId: { eq: $id }) {
title
content
slug
category {
title
slug
}
}
}
`;
Have you tried using your own rendering component (components)?
<ReactMarkdown
children={content}
components={{
u: ({node, ...props}) => <u style={{textDecoration: 'underline'}} {...props} />
}}
className={s.content}
transformImageUri={uri => uri.startsWith("http") ? uri : `${process.env.GATSBY_IMAGE_BASE_URL}${uri}`}
/>
More details about components property: https://github.com/remarkjs/react-markdown#appendix-b-components
You can even use a custom-styled <p> instead of <u> if needed. The idea relies on parsing the <u> tag to add your own underlined component.
Apparently, it is needed to install rehype-raw plugin to allow ReactMarkdown to understand and interpret underlined tags (<u>)
I'm trying to present data and
the json data contains html tags. so it's shown like this in the
browser
<ul><li>Marcus</li><li>19year old</li></ul>
it literally contains html tags as text.
data['id'].map((v)=>(
<div>{v.name}</div>
<div>{v.age}</div>
))
in react how can I show only text?
Ideally you would show us an example of the json data, without seeing that this answer might not be that helpful.
You can use dangerouslySetInnerHTML to render a html string as if it were actually HTML:
const data = [
{ name: "<li>Marcus</li>", age: "<li>19 year old</li>" },
{ name: "<li>Jane</li>", age: "<li>22 year old</li>" }
];
export default function App() {
return (
<div className="App">
{data.map((v) => (
<>
<div dangerouslySetInnerHTML={{ __html: v.name }} />
<div dangerouslySetInnerHTML={{ __html: v.age }} />
</>
))}
</div>
);
}
Codesandbox:
https://codesandbox.io/s/wandering-dream-vhprd?file=/src/App.js
I solved it by using
yarn add react-html-parser
I got an advice from here
Render HTML string as real HTML in a React component
you can use this like below.
import ReactHtmlParser from 'react-html-parser';
const Component = (props) => {
const {data} = props;
return(
<div>{ReactHtmlParser(data.title)}</div>
{/* this will show bold text of hello instead of <b>hello</b> or any
other html form of json data */}
)
}
Now i use visual studio code to do my project. I can build my code without error, but when running, it no show out the content for html file, only have show css like header abd footer. i have click button on header but cannot go to other page.Here is the sample code
code in index.html
<nav>
List
New student
Student feedback
</nav>
Vue router
const router = new VueRouter({
routes: [
{ path: '/home', component: load('home') },
{ path: '/insert', component: load('insert') },
{ path: '/update/:id', component: load('update') },
{ path: '/feedback', component: load('feedback') },
{ path: '*', redirect: '/home' }
]
});
File name and type: _home.html, _insert.html, _update.html, _feedback.html
Can help me see the problem, thank you
I don't think you should edit directly to index.html as Vue is Single Page Application (SPA) framework. Instead, you should use Vue Component for each page.
This video might help you to figure out how to use Vue and Vue Router properly: https://youtu.be/nnVVOe7qdeQ
Edit:
For sake of clarity, Let me build simplified diagram of Vue project for you.
First of all, make sure you create the project via vue cli. It guides you to build your new vue project better.
Let's say we have 3 pages:
Home
About
Another
Each page has its own CSS, HTML (we call it template), and JavaScript in one file, the .vue file. To connect them, we need a first entrance, main.js. Inside of it, we can configure the router.
Inside main.js
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import HomePage from "./HomePage.vue";
import AboutPage from "./AboutPage.vue";
import AnotherPage from "./AnotherPage.vue";
// This is your router configuration
Vue.use(VueRouter);
const router = new VueRouter({
[
{ path: "/", component: HomePage },
{ path: "/about", component: AboutPage },
{ path: "/another", component: AnotherPage },
],
mode: "history",
});
// Initialize Vue App
new Vue({
router,
render: h => h(App),
}).$mount("#app");
Then, we need to create App.vue and put <router-view /> inside of it.
Inside App.vue source file
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
// Keep this empty. Except if you
// need to add sidebar or any else.
}
</script>
Now you're ready to create those three pages
Every pages looks like this:
<style scoped>
// Your CSS here
</style>
<template>
<div>
<!-- Your HTML here -->
</div>
</template>
<script>
export default {
data() {
return {
// Your reactive data here
}
},
mounted() {
// Your script here
},
methods: {
// Your functions here
},
}
</script>
That's all I can explain, hope it helps. If I missed something, please don't hesitate to tell me. Thank you!
I'm drafting a VueJS app which allows users to insert an email HTML template, do some processing and download the processed template afterwards.
I have two components and trying to render the HTML via the HTMLViewer component with v-html function as follows:
Home:
<template>
<div>
<mdb-container>
<mdb-row>
<mdb-col>
<div class="form-group">
<mdb-input
type="textarea"
label="Paste your HTML here:"
outline
:rows="25"
v-model="emailTemplate"
/>
</div>
</mdb-col>
<mdb-col>
<HTMLViewer :emailTemplate="emailTemplate" />
</mdb-col>
</mdb-row>
<mdb-row class="ml-1">
<mdb-btn outline="default">Next</mdb-btn>
<mdb-btn outline="danger" #click="clearEmailTemplate">Clear</mdb-btn>
</mdb-row>
</mdb-container>
</div>
</template>
<script>
import { mdbContainer, mdbRow, mdbCol, mdbInput, mdbBtn } from 'mdbvue';
import HTMLViewer from '../components/HTMLViewer';
export default {
name: 'HomePage',
components: {
mdbContainer,
mdbRow,
mdbCol,
mdbInput,
mdbBtn,
HTMLViewer
},
data() {
return {
emailTemplate: '',
};
},
methods: {
clearEmailTemplate() {
this.emailTemplate = null;
this.iframeVisible = false;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
HTMLViewer:
<template>
<div v-html="emailTemplate">
</div>
</template>
<script>
import { mdbContainer, mdbRow, mdbCol, mdbInput, mdbBtn } from 'mdbvue';
export default {
name: 'HTMLViewer',
components: {
mdbContainer,
mdbRow,
mdbCol,
mdbInput,
mdbBtn
},
props:['emailTemplate'],
data() {
return {
};
},
};
</script>
<style scoped>
</style>
The problem I'm facing is the fact that the email template CSS styles overwrite the ones from the parent component (Home).
I tried to use iFrame which solves this issue but also creates a lot of different problems related to interacting with the HTML template, therefore I would like to avoid iframe as much as possible.
My question is - is there a good way to make sure the inserted email template HTML does not affect the global styles?
I'm an angular novice, currently building an angular2 app.
What I want to do is generate a series of DOM components from the following object data:
// Class construct with alphabeticalized properties
export class Screens {
screens: Array<Object>;
}
export var screenData: Screens = {
// Lists all of the audio files in the course
screens: [
{
id: 0,
template: 'templateURL-0.html',
css: 'templateURL-0.css'
},
{
id: 1,
template: 'templateURL-1.html',
css: 'templateURL-1.css'
},
{
id: 2,
template: 'templateURL-0.html',
css: 'templateURL-0.css'
}
]
};
I want the end result to be something similar to the following where template 0 will be displayed twice, and template 1 once; in order:
<app-screen></app-screen> <!-- templateURL-0.html content -->
<app-screen></app-screen> <!-- templateURL-1.html content -->
<app-screen></app-screen> <!-- templateURL-0.html content -->
I read the tutorial on Structural Directives and I think I need to implement something along those lines, however I'm honestly feeling a little lost on the best approach.
Ideally I would like to have something like:
<app-screen *ngFor="let screen of screenData.screens"></app-screen>
Which would then somehow set the template URL depending on what screenData.screens.template is.
Or should I do something like this? (unsure if correct syntax)
<div *ngFor="let screen of screenData.screens" [ngSwitch]="screenData.screens.template">
<app-screen-template1 [ngSwitchCase]="'templateURL-0.html'"></app-screen-template1>
<app-screen-template2 [ngSwitchCase]="'templateURL-1.html'">Ready</app-screen-template2>
</div>
Note: I will never change the templateURL reference.
I found that the best method to achieve this is to implement routing with the built in RouterModule.
So in the end I have the following in my class, where the template property is a url path / url segment.
// Class construct with alphabeticalized properties
export class Screens {
screens: Array<Object>;
}
export var screenData: Screens = {
// Lists all of the audio files in the course
screens: [
{
id: 0,
template: 'template/template-0'
}
]
};
Then when I want to load / instantiate this template, all I have to do is navigate to this url using something like:
<!-- Goes to localhost:4200/template/template-0 -->
<button [routerLink]="[screen.template]"></button>
Where screenis a bound variable in my .ts.
More on routing and navigation here.