Newbie hacking my way through. I'm trying to pull rows from an array and display them in a screen. I have the array populated and the snippet of code below works:
return (
buildings.map((building) => {
return <div className="col-md-9">
{building.name}
But when I try to use a component in the statement I get an error. This doesn't work:
return (
buildings.map((building) => {
return <div className="col-md-9">
<Building building={building.name}/>
The Building component is here:
import React from 'react'
function Building({building}) {
return (
<div className="row">
<h1>{building.name}</h1>
</div>
)
}
export default Building
The error I get is here:
src/screens/Homescreen.js Line 34:15: 'Building' is not defined
react/jsx-no-undef
I can provide more details if necessary. I think I'm missing something obvious but I'm stuck at the moment so any help is appreciated.
First check to see if you are importing Building component.
secondly, <Building building={building.name}/> you are passing down the name attribute of building and then in the Building component, trying to access that name attribute. This should be changed to <Building building={building}/>. Fix these to see if the problem continues to exist.
Related
Well My purpose is to use checkbox to pick a template,after that it will be added to a database
but when i try to get the code of template into value it always display undefined.
Interface
Html Code:
<input type="checkbox" id="templatepick" name="picker" value="item.templatebodyclient" (change)="displaydata($event)">
templatebody attribut have the html of our template so i tried to get it from the checked template value to be set in database
Typescript code:
displaydata(event: { checked: any; }){
console.log("",event.checked) }
Any idea how i can solve this ? or another idea could be useful and thanks
You may use:
HTML
<input type="checkbox" id="templatepick" name="picker" [value]="item.templatebodyclient" (change)="displaydata($event)">
TS
displaydata(event: any) {
console.log(event.currentTarget.checked)
console.log(event.target.checked)
}
I have this code for handling status changes in my model.
<Form.Item
name="status"
label="Status"
>
<Select>
{
statusOptions
?
Object.keys(statusOptions).map(statusId => {
return <Select.Option key={ statusId } value={ statusId }>{ statusKey2Value(statusId) }</Select.Option>;
})
:
null
}
</Select>
</Form.Item>
statusOptions is a JSON object as follows:
{
0: Deleted
1: Active
}
I am using Antd v4.5.1 which recommends handling initialValues directly within the Form component. I basically retrieve the data from the backend and provide the data to the Form component. However, when I try to set selected option for the dropdown, it is being displayed as "1" instead of "Active". I have found a solution provided by Antd Select API which is to hold status value as an object with a label value inside but I didn't really find it optimal to change my backend structure. Can anyone help me with this by any chance?
Try using an optionLabelProp="label" on Select component and place a label={statusOptions[statusId]} on Select.Option. Oh and make Select.Option self-closing.
I am taking a full stack course via Eduonix.com. It seems that some of the code syntax is deprecated, as I have had to install older versions of multiple things to get through some of the sections. This did not help when I came to the Meteor.js section, so I installed the most recent and did some searching that allowed me to get through the first lesson. I tried the same for the second lesson (when I encountered this error), but have not had any luck with anything I have found.
I get this error when I try to use
todos.find().fetch()
in the browser's console.
Relevant File Structure:
client
--main.html
--main.js
lib
--collections.js
View on github
In the course lesson, the line
import { Todos } from '../lib/collections';
is not present in main.js and the line
export const Todos = new Mongo.Collection('todos');
in collections.js is presented as
Todos = new Mongo.Collection('todos');
I have tried changing
import { Todos } from '../lib/collections';
to
import { Todos } from '/lib/collections';
but it did nothing.
I have also tried just adding
Todos = new Mongo.Collection('todos');
to main.js, but I got an error that said "todos" was already defined (only to get the same undefined error when I tried to run the console command, because somehow it was both already defined and is still undefined).
I have made these changes based on looking up similar issues online before making this post, hoping that it would save me like adding
import './main.html';
to main.js did when I was getting a similar undefined error related to that prior to this error.
main.html
<head>
<title>QuickTodos</title>
</head>
<body>
{{> main}}
</body>
<Template name="main">
<header>
<h1>{{title}}</h1>
</header>
<ul>
{{#each todos}}
{{> todo}}
{{/each}}
</ul>
</template>
<template name="todo">
<li>{{text}}</li>
</Template>
main.js
import { Template } from 'meteor/templating';
import './main.html';
import { Todos } from '../lib/collections';
const todos = [
{text:'Pickup kids from school'},
{text:'Go food shopping'},
{text:'Meeting with boss'}
];
Template.main.helpers({
title(){
return 'QuickTodos';
},
todos(){
return todos;
}
});
collections.js
import {Mongo} from 'meteor/mongo';
export const Todos = new Mongo.Collection('todos');
When I run
todos.find().fetch()
I expect to get an empty array, but instead I get:
VM1905:1
Uncaught ReferenceError: todos is not defined
at <anonymous>:1:1
What am I doing wrong?
The first thing I notice is that the capitalization of these two lines are different, meaning they will point to different variables (thus, todos is not defined):
export const Todos = new Mongo.Collection('todos');
todos.find().fetch()
The second thing, is that because Meteor uses CommonJS modules, any variables you declare will be local to that module, and won't be available on the console.
(there's some exceptions, like the Todos declared without var, let, or const, which will be scoped across the whole application, but still not global or available in the console)
You can force a variable to be global by adding it as a property to the window:
import { Mongo } from 'meteor/mongo';
export const Todos = new Mongo.Collection('todos');
window.Todos = Todos; // here we make it global
And then as long as this file is imported by client/main.js, you can use Todos on the console.
This is fine for small apps and testing stuff, but increases the likelihood of name collisions and makes it harder to understand where code is coming from in larger apps.
If you want to do things the "right" way for a module system, you can access the exports of a module in the console with require:
> Todos = require('/lib/collections').Todos
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>
Just went through the Tour of Heroes tutorial app and experienced some interesting behavior within my template. I started the second part of the tutorial with the following code:
class Hero {
id: number;
name: string;
}
#Component({
selector: 'my-app',
template:`
<h1>{{title}}</h1>
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<div><input [(ng-model)]="hero.name" placeholder="name"></div>
</div>
`,
directives: [FORM_DIRECTIVES]
})
class AppComponent {
public title = 'Tour of Heroes';
public hero: Hero = {
id: 1,
name: 'Windstorm'
};
}
When instructed to add the array of new heroes (var HEROES: Hero[] = [ /* Hero Data */];) and the new property to my component, I assumed we were replacing the original hero property and ended up with this:
class AppComponent {
public title = 'Tour of Heroes';
public heroes = HEROES;
}
Next, the template was modified like so:
<h1>{{title}}</h1>
<ul class="heroes">
<li *ng-for="#hero of heroes">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<h2>{{hero.name}} details!</h2>
<div><label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<div><input [(ng-model)]="hero.name" placeholder="name"></div>
</div>
In the browser, the unordered list then rendered one li per hero in the array, but did not print the name or id. Crazy. After some tinkering, I realized that if I added the original hero property back to the AppComponent class all of the heroes in the array rendered just fine. Also, if I simply removed any template code referencing the hero property not in the ng-for loop the list would also render just fine.
Here is what I expected:
In my original version, all of the heroes in the array should be reflected in the unordered list, but then all of the hero values outside of the loop should be undefined or possibly the last item in the list.
When I added back the original hero property, there should be some sort of name collision or some other side effect.
How does this work the way it does?
Edit: Here is the requested plunker: http://plnkr.co/edit/U3bSCaIOOjFdtw9XxvdR?p=preview
Ok so with your plunker I got a bit more of what you were trying to do and the issues you were having.
My Working plunk with more details is HERE
1. NG-For
When you do a NG-For loop the "#" variable is isolated to be inside the loop only. I renamed it "domhero" so you can see.
You had a few calls to it OUTSIDE of the li object which wouldn't work. I re-wrote it a bit to put all your titles and stuff inside the LI loop.
2. Variables from the object
On the input you were trying to access a variable from inside the ng-for loop which you cant do. Once you close out of a loop you cant access those variables. So I showed where I was binding to, to make it clearer for you.
I think it got confusing when you had so many things named the same thing all over the place (hero, heroes, HEROES, class: hero) if you take a look at the plunker I made I renamed the variables to help mark where they are coming from.
Hope it helps!
p