I draw tabs bootstrap using react.js. And then I was at the event onŠ”lick content first tab, I create another tab. But when I try to switch tabs, I do not see the contents of the dynamic creation of the tab.
I understand that is not assigned to the class of the class of active tab-pane. But I do not understand what I'm doing wrong? It is not dynamically created tabs are switched. What could be the problem?
Code on codepen: https://codepen.io/alex183/pen/apOKxP?editors=0010
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
newTab:false, id:'', name:'', numTab:0,
currentTab:"home",
};
}
changeTab(e) {
this.setState({ currentTab: e.target.getAttribute('aria-controls') });
}
render() {
const tabs = [], tabsContent = [];
for(let i=0; i < this.state.numTab; i+= 1){
console.log('for->', this.state);
let href = "#"+this.state.id;
let tcClass = this.state.currentTab == this.state.id ? "tab-pane active" : "tab-pane";
tabs.push(<li role="presentation">{this.state.name}</li>);
tabsContent.push(<div role="tabpanel" className="tab-pane active" id={this.state.id}>{this.state.id} - {this.state.name}</div>);
}
return(
<div>
<ul className="nav nav-tabs" role="tablist">
<li role="presentation" className="active"><a onClick={this.changeTab.bind(this)} href="#home" aria-controls="home" role="tab" data-toggle="tab">Home</a></li>
<li role="presentation">Profile</li>
<li role="presentation">Mes</li>
{tabs}
</ul>
<div className="tab-content">
<div role="tabpanel" className="tab-pane active" id="home"><h1 onClick={this.addTab.bind(this)} id="obj">123</h1></div>
<div role="tabpanel" className="tab-pane" id="profile">456</div>
<div role="tabpanel" className="tab-pane" id="mes">789</div>
{tabsContent}
</div>
</div>
);
}
addTab(e){
let id = e.target.getAttribute('id');
let name = e.currentTarget.textContent;
this.setState({newTab:true, id:id, name:name, numTab:this.state.numTab+1});
console.log('addTab->', this.state);
}
}
React.render(<App />, document.body);
UPD
I just created three tabs, and they work fine.
I click on the contents of the first tab.
And dynamically creates
another tab.
When I switch
to the last created tab I can see the contents of the first tab how
to fix it?
You have many problems in you code.
First, you can't use id of your h1 element to pass tab-content id to addTab, because the tab content must have a unique id. You can use data-attribute to pass data, or add arguments to your binding (this.addTab.bind(this, "obj")).
Here is your code corrected :
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
newTab:false, id:'', name:'', numTab:0
}
}
render() {
const tabs = [], tabsContent = [];
for(let i=0; i < this.state.numTab; i+= 1){
console.log('for->', this.state);
let href = "#"+this.state.id;
tabs.push(<li role="presentation">{this.state.name}</li>);
tabsContent.push(<div role="tabpanel" className="tab-pane" id={this.state.id}>{this.state.id} - {this.state.name}</div>);
}
return(
<div>
<ul className="nav nav-tabs" role="tablist">
<li role="presentation" className="active">Home</li>
<li role="presentation">Profile</li>
<li role="presentation">Mes</li>
{tabs}
</ul>
<div className="tab-content">
<div role="tabpanel" className="tab-pane active" id="home">
<h1 onClick={this.addTab.bind(this)} data-id="obj">Add new tab</h1>
</div>
<div role="tabpanel" className="tab-pane" id="profile">456</div>
<div role="tabpanel" className="tab-pane" id="mes">789</div>
{tabsContent}
</div>
</div>
);
}
addTab(e){
let id = e.currentTarget.dataset.id;
let name = e.currentTarget.textContent;
this.setState({newTab:true, id:id, name:name, numTab:this.state.numTab+1});
console.log('addTab->', this.state);
}
}
React.render(<App />, document.body);
Related
I need to highlight selected tabs in the navigation tab wizard. tab functionality is working fine..but I need to add the CSS(border-style) for all the selected tab. for example, if I have 4 tabs. If I have selected the first and second tab(next button) those tab needs to be highlighted(apply border) and again if I click the back button the highlight needs to be removed.Any help would be appreciated. please find the image below for reference.
public tabs: string[] = ['tab1', 'tab2', 'tabs3', 'tabs4']
public next(): void {
const currentIndex: number = this.tabs.indexOf(this.selectedTab);
const newIndex = currentIndex + 1;
this.selectedTab = this.tabs[newIndex];
}
public back(): void {
const currentIndex: number = this.tabs.indexOf(this.selectedTab);
const newIndex = currentIndex - 1;
this.selectedTab = this.tabs[newIndex];
}
.tab-highlight {
border-top: 2px solid blue;
cursor: pointer;
}
<ul class="nav nav-tabs side_nav" role="tablist">
<li *ngFor="let tab of tabs; let i= index;" [attr.data-index]="i" role="presentation"
class="accounts_li"
[class.active]="selectedTab===tab" (click)="toggle($event)">
<a (click)="selectedTab = tab" aria-controls="one" role="tab" data-toggle="tab">{{tab}}</a>
</li></ul>
<div class="tab-content">
<div id="tab1" *ngIf="selectedTab === tabs[0]" role="tabpanel" class="tab-pane active">
tab1 content
</div>
<div id="tab2" *ngIf="selectedTab === tabs[1]" role="tabpanel" class="tab-pane active">
tab2 content
</div>
<div id="tab3" *ngIf="selectedTab === tabs[2]" role="tabpanel" class="tab-pane active">
tab3 content
</div>
</div>
<button class="button" (click)="back()"><span>
Back</span>
</button>
<button class="button" (click)="next()"><span>
Next</span>
</button>
image
If you want to select multiple tabs then you should change the selectedTab property with an array which will hold all selected tabs. Then you could create a method which will check if the tab is selected or not in order to push/remove from the selected array. Lastly, for the conditional class name you could use [ngClass]="isSelected(tab) ? 'tab-highlight' : null", this will add the class only if the condition is evaluated to true (more on this here https://angular.io/api/common/NgClass)
Component code:
tabs: string[] = ["tab1", "tab2", "tab3", "tab4"];
selectedTabs: string[] = [];
onTabClick(tab) {
if (this.isSelected(tab)) {
let index = this.selectedTabs.indexOf(tab);
this.selectedTabs.splice(index, 1);
} else {
this.selectedTabs.push(tab);
}
}
isSelected(tab: string) {
return this.selectedTabs.indexOf(tab) > -1;
}
HTML code for the tab menu:
<ul class="nav nav-tabs side_nav" role="tablist">
<li *ngFor="let tab of tabs; let i= index;" [attr.data-index]="i" role="presentation"
[ngClass]="isSelected(tab) ? 'tab-highlight' : null">
<a (click)="onTabClick(tab)" aria-controls="one" role="tab" data-toggle="tab">{{tab}}</a>
</li>
</ul>
Stackblitz: https://stackblitz.com/edit/question63192497?file=src%2Fapp%2Fapp.component.ts
When I select a item (ul,li),
I need to update the view component dynamically,but cannot achieve.
How do I modify the code ?
Do not use (Controller)"return view" method
(Partial code)
(chtml)
<div>
<ul class="nav nav-tabs">
<li role="tab"> <a href="#" onclick=change1();>1</a></li>
<li role="tab"> <a href="#" onclick=change2();>2</a></li>
</ul>
</div>
<div role="tabpanel" class="tab-pane active">
#{
string item = ViewBag.NowPay;
switch (item)
{
case "Card1":
#await Component.InvokeAsync("DownloadConfig1", new { reconciliationConfig = Model });
break;
case "Card2":
#await Component.InvokeAsync("DownloadConfig2", new { reconciliationConfig = Model });
break;
default:
break;
}
}
</div>
(script)
function change1() {
$.ajax({
type: 'POST',
url: '/ReconciliationConfig/TestChane1',
success: function (data) {
}
})
}
Controller
public String TestChane1()
{
ViewBag.NowPay = "Card1";
var Result = ViewBag.NowPay;
return Result;
}
Thanks for answer
First Solution
if you don't want to go again to server, you have to render each case in separate div and show/hide with javascript
View Code cshtml
<style>
.d-none, .hidden { display: none; }
</style>
<div>
<ul class="nav nav-tabs">
<li role="tab"> <a href="#" onclick=change('Card1');>1</a></li>
<li role="tab"> <a href="#" onclick=change('Card2');>2</a></li>
</ul>
</div>
<div role="tabpanel" class="tab-pane active">
#{
string item = ViewBag.NowPay;
}
<div id="Card1" class="cards #(item == "Card1" ? "" : "hidden d-none")">
#await Component.InvokeAsync("DownloadConfig1", new { reconciliationConfig = Model });
</div>
<div id="Card2" class="cards #(item == "Card2" ? "" : "hidden d-none")">
#await Component.InvokeAsync("DownloadConfig2", new { reconciliationConfig = Model });
</div>
</div>
<script>
function change(name) {
$('.cards').addClass('hidden d-none'); // hide all cards
$('#'+ name).removeClass('hidden d-none'); // show only one
}
</script>
Not currently successful
cshtml
<style>
.d-none, .hidden { display: none; }
</style>
<div>
<ul class="nav nav-tabs">
<li role="tab"> <a href="#" onclick=changeCard1();>1</a></li>
<li role="tab"> <a href="#" onclick=changeCard2();>2</a></li>
</ul>
</div>
<div role="tabpanel" class="tab-pane active">
<div id="Card1" class="d-none">
#await Component.InvokeAsync("DownloadConfig1", new { reconciliationConfig = Model });
</div>
<div id="Card2" class="d-none">
#await Component.InvokeAsync("DownloadConfig2", new { reconciliationConfig = Model });
</div>
</div>
<script>
function changeCard1() {
$('Card1').removeClass('d-none'); // show only one
$('Card2').addClass('d-none');
}
function changeCard2() {
$('Card1').addClass('d-none'); // show only one
$('Card2').removeClass('d-none'); // show only one
}
</script>
I'm trying to loop through some elements with an attribute and based on which element I click, I'd like the is-active class to be added to the specific element.
The top section works, clicking on the specific tab, but the problem is with tabBody.
Right now, it adds the class to ALL elements and I cannot figure out where to put this to reference the correct div.
I just want whichever tab-body is active, to be the same tabs-bg is active, and have all that based on which tab is clicked.
I have this code (I've tried let el = this, says already defined):
const tab = document.querySelectorAll('[data-tabs-tab]');
const tabBody = document.querySelectorAll('[data-tabs-body]');
function changeMe() {
const activeTab = this.hasAttribute('data-tabs-tab');
[...tab].forEach(el => {
if (this !== el) {
el.classList.remove('is-active');
}
});
this.classList.add('is-active');
[...tabBody].forEach(el => {
if (el.hasAttribute('data-tabs-body') == activeTab) {
if (el.classList.contains('tabs-bg')) {
el.classList.add('is-active');
}
} else {
el.classList.remove('is-active');
}
});
}
tab.forEach(el => el.addEventListener('click', changeMe));
<div class="tabs" data-tabs>
<nav>
<ul class="tabs__tabs">
<li data-tabs-tab="one" class="is-active"></li>
<li data-tabs-tab="two"></li>
<li data-tabs-tab="three"></li>
</ul>
</nav>
<div class="tabs__content">
<div data-tabs-body="one" class="tab-body is-active"></div>
<div data-tabs-body="two" class="tab-body"></div>
<div data-tabs-body="three" class="tab-body"></div>
</div>
</div>
<div class="tabs__bg">
<div data-tabs-body="one" class="tabs-bg is-active"></div>
<div data-tabs-body="two" class="tabs-bg"></div>
<div data-tabs-body="three" class="tabs-bg"></div>
</div>
.hasAttribute() just checks to see if the attribute exists on the element. You should be using .getAttribute() instead to get the value of the attribute.
Also, not sure why the if (el.classList.contains('tabs-bg')) block was in there, but I commented it out to make the demo work.
const tab = document.querySelectorAll('[data-tabs-tab]');
const tabBody = document.querySelectorAll('[data-tabs-body]');
function changeMe() {
const activeTab = this.getAttribute('data-tabs-tab'); // Change here
[...tab].forEach(el => {
if (this !== el) {
el.classList.remove('is-active');
}
});
this.classList.add('is-active');
[...tabBody].forEach(el => {
if (el.getAttribute('data-tabs-body') === activeTab) { // and here
//if (el.classList.contains('tabs-bg')) {
el.classList.add('is-active');
//}
} else {
el.classList.remove('is-active');
}
});
}
tab.forEach(el => el.addEventListener('click', changeMe));
.is-active {color: red;}
<div class="tabs" data-tabs>
<nav>
<ul class="tabs__tabs">
<li data-tabs-tab="one" class="is-active">Tab 1</li>
<li data-tabs-tab="two">Tab 2</li>
<li data-tabs-tab="three">Tab 3</li>
</ul>
</nav>
<div class="tabs__content">
<div data-tabs-body="one" class="tab-body is-active">Tab Body Content 1</div>
<div data-tabs-body="two" class="tab-body">Tab Body Content 2</div>
<div data-tabs-body="three" class="tab-body">Tab Body Content 3</div>
</div>
</div>
<div class="tabs__bg">
<div data-tabs-body="one" class="tabs-bg is-active">Tab Body BG 1</div>
<div data-tabs-body="two" class="tabs-bg">Tab Body BG 2</div>
<div data-tabs-body="three" class="tabs-bg">Tab Body BG 3</div>
</div>
I am pretty new to developing in React and Javascript. I am bulding an application with a side menu. I wanted to display different components based on the selection in side menu. Can someone guide me how to do this. I am attaching my menu component and index code here.
class AppIndex extends Component {
constructor(props) {
super(props);
}
render () {
return (
<Navbar />
<div className="row">
<div className="col-md-2">
<MenuComponent/>
</div>
<div className="col-md-10">
// I need to add the components here based on selected item in menu
</div>
</div>
);
}
}
class MenuComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ul className=" nav nav-pills mr-auto flex-column">
<li className="nav-item">
Overview
</li>
<li className="nav-item">
Component1
</li>
<li className="nav-item">
Component2
</li>
<li className="nav-item">
Component3
</li>
<li className="nav-item">
Component4
</li>
<li className="nav-item">
Component5
</li>
</ul>
);
}
}
im sorry i didnt see your top level component.
Now from my experience, i wanna give you a little advice, don't let any child component render another component. try to avoid that as much as possible.
Here's what i would do.
This is the Top Level Component
class AppIndex extends Component {
constructor(props) {
super(props);
this.state = { component: '' };
this.selectComponent = this.selectComponent.bind(this); // dont forget to bind
}
selectComponent(event){ // this will take the name of the button thats beeing clicked and sets name of button to state
event.preventDefault();
this.setState({component: event.target.name});
}
render () {
let toRender = null;
switch(this.state.component)
{
case "component 1":
toRender = <Component1/>
case "component 2":
toRender = <Component2/>
case "component 3":
toRender = <Component3/>
}
return (
<Navbar />
<div className="row">
<div className="col-md-2">
<MenuComponent onClick = {this.selectComponent}/>
</div>
<div className="col-md-10">
{toRender} //here goes the component
</div>
</div>
);
}
}
this is the menu
const MenuComponent = ({onClick}) => { // you dont need a class component for this
return (
<ul className=" nav nav-pills mr-auto flex-column">
<li className="nav-item">Overview</li>
<li className="nav-item"> <button onClick = {onClick} name = "component 1">Component1</button></li>
<li className="nav-item"> <button onClick = {onClick} name = "component 2">Component2</button></li>
<li className="nav-item"> <button onClick = {onClick} name = "component 3">Component3</button></li>
</ul>
);
}
thats it.
easy.. You already have the name of the button in the top level component's state right?
send it down as props to the MenuComponent and on every button write something like,
here im assuming the name of the props the MenuComponent get is name
<li className="nav-item"> <button className = {this.props.name === "component 1"?"Active":""} onClick = {onClick} name = "component 1">Component1</button></li>
I am using a bootstrap tabpanel like this:
<div class="row spiff_tabs_body">
<!-- Nav tabs -->
<ul class="nav nav-tabs spiff_tabs" role="tablist">
<li role="presentation" class="active">
Potential Spiff
</li>
<li role="presentation">
Instant Spiff
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="delayedspiff"></div>
<div role="tabpanel" class="tab-pane" id="instantspiff"></div>
</div>
</div>
</div>
</div>
Using javascript I need to figure out which tab the user is on. I'm not really sure how to do this. I've read a lot about selecting the active tab, but I can't really find out how to check which tab the user has selected.
Updated:
I forgot to mention I need to be able to check in a conditional if statement which tab is selected and then do something based on which tab is active.
I need to do something like this:
<script>
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
// here is the new selected tab id
var selectedTabId = e.target.id;
});
var id = $('.tab-content .active').attr('id');
if (id == "instantspiff") {
alert("instantspiff");
}
else {
alert("delayedspiff");
}
</script>
check this
var id = $('.tab-content .active').attr('id');
if(id == "delayedspiff") {
alert("delayedspiff");
}
else {
alert("instantspiff");
}
when they click on a tab add this
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
// here is the new selected tab id
var selectedTabId = e.target.id;
});