Background info:
I'm using react and Ant Design.
To keep the code clean, I populate menu items from a const array, like so:
const menuItems = [
{ label: "Home", path: "/home" },
{ label: "Accounts", path: "/accounts" },
{ label: "Organizations", path: "/organizations" },
];
Each item in the array is an object containing a label and a redirect path. I map over the items when rendering. Very basic.
Problem:
I would like to include an antd icon component in the menuItems array so the icon can be rendered next to the label. But I can't find a way to reference the icons by a name string
My problem is like this problem but is ant design
Rendering Material-UI icons from an array
Any suggestions on how to do this? Thanks
you can modify your menuItems to something like this:
const menuItems = [
{ label: "Home", path: "/home", icon: <span class="custom-icon" /> },
{
label: "Accounts",
path: "/accounts",
icon: <span class="custom-icon" />
},
{
label: "Organizations",
path: "/organizations",
icon: <span class="custom-icon" />
}
];
and instead of using span with the class of custom-icon you can use any Icon you desire and then render it accordingly
Related
I have a Vue tab component that console logs out the correct index of the selected tab but I don't know how to set the active tab so it is opened. Any help or direction would be appreciated.
Component:
<Tabs
:tabs="tabs"
:active-tab-index="activeTab"
#tab-changed="changeTab"
/>
Markup
<div v-show="activeTab === 0">
Show tab one content
</div>
<div v-show="activeTab === 1">
Show tab two content
</div>
Data:
data() {
return {
tabs: [
{ id: 0, name: 'Tab One' },
{ id: 1, name: 'Tab Two' },
],
activeTab: 0,
}
},
Method:
changeTab(index) {
console.log('Change activeTab', index)
},
it's not completely clear to me where the method and data is, but I assume it's in the parent, so in that case you'd simply need to change the function to something like this:
changeTab(index) {
this.activeTab = index
},
your component could be even cleaner if it would provide option to use v-model on it like: v-model:activeTabIndex="activeTab". To make this happen you'd just need to emit the data inside the component with update:activeTabIndex instead of tab-changed.
I'm trying to create a vue js application where a treeview is displayed to the user. The elements inside the treeview can contain other elements, that can contain other elements etc ... With no limit, which means that the depth of the treeview is not known. If I'm right, it means I can't simply use the v-for directive (because it implies to know the depth right ?)
So i'm basically looping going through a json array and creating <ul> and <li> tags to append them to some other tag in the DOM, but if I do this, they don't get the styles of their class applied to them.
I suppose it's because Vue doesn't like the fact that we modify the DOM without having vue doing it for us.
Also, We don't want to use components libraries like vuetify, we want to do it with vue only and simple javascript.
Thank you !
This is actually pretty straight forward in Vue.js.
What you have to do is simply create a component that invokes itself but changing the v-for to use the current tree branch's children.
An important step for making this work in Vue is to apply the name key to the component. Otherwise, the component can not invoke itself.
I have provided a simple example below using HTML's neat details element.
// Your recursive branch component "branch.vue"
const branch = {
name: 'branch',
props: {
branch: {
type: Object,
default: () => ({}),
},
},
template: `
<details>
<summary>{{ branch.title }}</summary>
<branch
v-for="branch in branch.children"
:key="branch.title"
:branch="branch"
/>
</details>
`,
}
// Your page view(component) where you want to display the tree
new Vue({
el: '#app',
name: 'tree',
components: {
branch,
},
data() {
return {
tree: [
{
title: 'parent 1',
children: [
{
title: 'child 1',
children: [
{
title: 'child 1-1',
children: [],
},
{
title: 'child 1-2',
children: [],
},
],
},
],
},
],
};
},
})
#app > details {
margin-left: 0;
}
details {
margin-left: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<branch v-for="branch in tree" :key="branch.title" :branch="branch" />
</div>
The solution is to create a recursive component, for example see here and
here
I made a menu component that accepts a json object with all menu itens.
For icons i use react-icons/io.
The json object is something like this:
const menus = {
Item1: { buttonText: 'Item 1 text', icon: { IoLogoAndroid }, path: 'item 1 path' },
Item2: { buttonText: 'Item 2 text', icon: { IoLogoAndroid }, path: 'item 2 path'},
};
This is the Menu function that will render the menu items as buttons:
const buttons = Object.keys(this.props.menus).map((menu) => {
return (
<a key={menu} href={this.props.menus[menu].path}
onClick={this.changeMenu.bind(this, menu)}>
{...this.props.menus[menu].icon} <- fail here
{this.props.menus[menu].buttonText}
</a>
);
})
I tried many ways to render the icon, but i am clueless on how this could work. Not sure if this is even possible. Any pointers?
If you are importing the icon from where you are defining the object then just tag it <IoLogoAndroid/>;, so react knows it should treat it as an element to render.
const menus = {
Item1: { buttonText: 'Item 1 text', icon: <IoLogoAndroid/> , path: 'item 1 path' },
Item2: { buttonText: 'Item 2 text', icon: <IoLogoAndroid/>, path: 'item 2 path'},
};
And then just call it directly (remove the ...)
<a key={menu} href={this.props.menus[menu].path}
onClick={this.changeMenu.bind(this, menu)}>
{this.props.menus[menu].icon}
{this.props.menus[menu].buttonText}
</a>
Alternatively you could just call React.createElement if you don't want to tag it in your object definition.
<a key={menu} href={this.props.menus[menu].path}
onClick={this.changeMenu.bind(this, menu)}>
{React.createElement(this.props.menus[menu].icon)}
{this.props.menus[menu].buttonText}
</a>
Here is a sample showing the 2 implementations https://codesandbox.io/s/pmvyyo33o0
I was just working with a similar project, and I managed to make it work, with a syntax like this
I here have an array of objects (like yours)
links: [
{
name: 'Frontend',
link: 'https://github.com/Thomas-Rosenkrans-Vestergaard/ca-3-web',
icon: <FaCode size={40} />,
id: 1
},
{
name: 'Backend',
link: 'https://github.com/Thomas-Rosenkrans-Vestergaard/ca-3-backend',
icon: <FaCogs size={40}/>,
id: 2
},
{
name: 'Mobile',
link: 'https://github.com/Thomas-Rosenkrans-Vestergaard/ca-3-app',
icon: <FaMobile size={40} />,
id: 3
}]
I then render my component by mapping, where I pass in the entire object as a prop
const projects = this.state.projects.map((project, i) => {
return(
<Project key={`Project key: ${i}`} project={project} />
)
})
I then use objectdestructing to get the prop
const { logo } = this.props.project
then it can just be displayed
//in my case I use antd framework, so I pass the FAIcon component in as a prop
<Meta
avatar={logo}
title={title}
description={description}
/>
I suppose you could do the same thing, by just passing the entire menu object in as a prop, and then accessing the icon?
You need to change the following:
icon: <IoLogoAndroid />
And in the code (remove the spread operator):
this.props.menus[menu].icon
Also, a few refactoring suggestions.
Do you really need to have an object of objects? Why not an array of objects, where every item has a "name" prop? It will be easier to iterate through, as you can access props directly from map, unlike with object keys.
You are creating a button list, so you should have ul and li tags aswell.
Consider passing only a reference to onClick such as:
onClick={this.changeMenu}
If you need to pass data around, you should use dataset for that. Pass a name/path then find it inside the change handler to avoid rebinding inside every re-render.
Refactored suggestion with an array of objects
changeMenu = e => {
const { menus } = this.props;
const { menuName } = e.target.dataset;
const menu = menus.find(menu => menu.name === menuName);
// Do something with menu
return;
};
renderMenu() {
return (
<ul>
{this.props.menus.map(menu => (
<li key={menu.name} style={{ listStyle: "none" }}>
<a
data-menu-name={menu.name}
href={menu.path}
onClick={this.changeMenu}
>
{menu.icon}
{menu.buttonText}
</a>
</li>
))}
</ul>
);
}
I am making a form generator, which uses components in it for input fields, buttons etc. I want to be able to generate the form depending on what options I pass to it.
But I can't get it to render the components.
I tried to return pure HTML but that won't render the components.
I call the form generator from my Home.vue template where I want the form with an options object like this:
options: {
name: {
type: 'input',
label: 'Name'
},
submit: {
type: 'button',
label: 'Send'
}
}
In template:
<template>
<form-generator :options="options"></form-generator>
</template>
In the form generator component I have tried multiple things like:
<template>
{{ generateForm(this.options) }}
// ... or ...
<div v-html="generateForm(this.options)"></div>
</template>
I include all the components like:
import {
FormButton,
FormInput
} from './FormComponents'
Now the final part is how do I make FormInput render?
This does not work since it outputs the HTML literally:
methods: {
generateForm(options) {
// .. do stuff with options ..
var form = '<form-input />'
return form
}
}
Vue has a very simple way of generating dynamic components:
<component :is="dynamicComponentName"></component>
So I suggest you define the options as an array and set the type to be the component name:
options: [
{
type: 'FormInput',
propsData: {label: 'Name'}
},
{
type: 'FormButton',
propsData: {label: 'Send'}
}
]
Then use it in the form generator like this:
<component :is="option.type" v-for="option in options"></component>
You can also pass properties as you'd pass to ant other component, but since it's dynamic and every component has a different set of properties i would pass it as an object and each component would access the data it needs:
<component :is="option.type" v-for="option in options" :data="option.propsData"></component>
UPDATE
Since you don't have control of the components it requires a bit more manipulation:
For each component that requires text, add a text attribute in the options:
options: [
{
type: 'FormInput',
propsData: {label: 'Name'}
},
{
type: 'FormButton',
text: 'Send',
propsData: {label: 'Send'}
}
]
And then just use it in the component:
<component :is="option.type" v-for="option in options">{{option.text}}</component>
For passing attributes, I think you can pass it using v-bind and then it will automatically destructure them, so if a button accepts 2 props: rounded, color
the options would look like:
{
type: 'FormButton',
text: 'Button',
propsData: {rounded: true, color: '#bada55'}
}
and then the component:
<component :is="option.type" v-for="option in options" v-bind="option.propsData">{{option.text}}</component>
you can create an Array like this:
components_data: [
{
name: 'checkbox',
data: false
},
{
name: 'text',
data: 'Hello world!'
}
]
and then loop through this array inside of the <component>:
<component
v-for="(component,i) in components_data"
:key="i"
:is="component.name"
:data="component.data"
/>
this will create 2 component [<text>, <checkbox>] dynamically and give them data via props.
when you push new data like this components_data.push({name:'image',data: {url:'cat.jpg'}}) it will render a new component as <image :data="{url:'cat.jpg'}"/>
So I want to achieve the image below but with content sent from server.
We can set menu items
const items = [
{ key: 'editorials', active: true, name: 'Editorials' },
{ key: 'review', name: 'Reviews' },
{ key: 'events', name: 'Upcoming Events' },
]
//...
<Menu vertical color='black' items={items}>
</Menu>
However, I do not see how to nest them. Just setting item 'content' to some XML.
How do I create a menu with multiple nested sections in ReactJS\Semantic-UI in Javacript?
I would create the following components:
<MenuContainer /> -> our root component
<Menu></Menu> -> can render either itself (<Menu />) or an <Item /> component
<Item></Item> -> can render only a <li /> or smth
And suppose we have the following JSON coming from our server:
{
label: 'Some menu',
children: [{ label: 'Sub menu', children: [...] }, ...],
}
Let assume that when we find an array in our JSON, that means that we have to render a menu. If we have an object, we render a simple child. A rough algorithm would be:
const MenuContainer = ({items}) => ({
{items.map(item => item.items ? <Menu items={items} /> : <Item data={item} /> }
});
Is this something you are looking for?