How to Create Dynamic Elements Inside Global Tabs

How to create dynamic elements inside global tabs

Issues You are Facing

  1. If length of data is less than four I am providing a plus button to create on more tab (give it default name), but I am not getting any idea how can I create the new one.

Here you want to get a new name, i.e. a form on the page with a text input, random generate name, use a prompt as I did below. Call setData and use a functional state update to spread existing data into a new array and append a new data object.

const addNewTab = () => {
/**
* window.prompt is just a quick way to request input from user.
*/
const name = window.prompt('Enter Name');

/**
* logic to generate next id, but could be anything really
*/
const id = generateId();

if (name) {
setData((data) => [...data, new Data(id, name)]);
}
};

  1. When I click on side button to create one dive inside tab, and I go to other tab and come back again I want that created div should not gets deleted.

A couple things going on here. First, you defined data1 in the function body so it is reset every render cycle. Keep the data in state, data can be provided as initial state. Second, you need to add the created elements. Do this by updating state.

Move data1 out of the component and define local component state initialized with data (update all references in render from data to data).

const [data, setData] = useState(data1);

Similar approach as adding a new tab, get the new data name and title. Here we call setData and again use a functional state update, but this time we'll map the existing state to a new array. When the array index matches the active tab index this is the data object we need to updated. Spread its myData into a new array and append the new data object. If index isn't a match then just return the element.

const Create_element = () => {
/**
* window.prompt is just a quick way to request input from user.
*/
const data_name = window.prompt('Enter Data Name');
const data_title = window.prompt('Enter Data Title');

if (data_name && data_title) {
setData(data => data.map((el, i) => i === active_menu ? {
...el,
myData: [...el.myData, { data_name, data_title }],
} : el))
}
};

  1. Suppose I have 3 tabs (data) so when page loads the first tab data I want to show, but it is not showing up I need to click there only it is showing up.

Issue here is caused by keeping a separate nestedData state object that is only ever updated when a tab is clicked. When a new element is added to the active tab's myData array nestedData isn't updated. Solution here is to simply render the nested data directly in the UI.

Update the tab's onClick handler to simply set the active menu (index).

onClick={() => setactive_menu(index)}

Render the myData directly from the new data state.

data[active_menu].myData.map((li, index) => (...

  1. If the data is null then I can create up to 4 tabs this also I a=want some Idea to do it.

Conditionally rendering the ADD button will control this.

{data.length < 4 && <button onClick={addNewTab}>ADD</button>}

Edit how-to-create-dynamic-elements-inside-global-tabs

Full Code

import React, { useState } from "react";
import "./styles.css";

import RightBar from "./Right_option";
import NEsted from "./Nested";

import "bootstrap/dist/css/bootstrap.min.css";

/**
* https://stackoverflow.com/questions/63814645/how-to-create-dynamic-elements-inside-global-tabs
*/

const data1 = [
{
id: 1,
name: "Maxi",
myData: [
{
data_name: "div1",
data_title: "div1 tittle"
},
{
data_name: "div1",
data_title: "div tittle"
}
]
},
{
id: 2,
name: "Phill",
myData: [
{
data_name: "div21",
data_title: "div21 tittle"
}
]
}
];

/**
* Data object constructor
* @param {number} id new data object id
* @param {string} name new data object name
* @example
* new Data(1, 'Bill')
*/
const Data = (id, name) => ({ id, name, myData: [] });

/**
* Id generator hook
* @param {number} [seed=0] initial id generator value
* @returns {function}
*/
const useIdGenerator = (seed = 0) => {
function* genId(seed = 0) {
let i = seed;
while (true) yield i++;
}

const generator = genId(seed);

return () => generator.next().value;
};

export default function App() {
const generateId = useIdGenerator(5);

const [data, setData] = useState(data1);
const [active_menu, setactive_menu] = useState(0);

const Create_element = () => {
/**
* window.prompt is just a quick way to request input from user.
*/
const data_name = window.prompt("Enter Data Name");
const data_title = window.prompt("Enter Data Title");

if (data_name && data_title) {
setData((data) =>
data.map((el, i) =>
i === active_menu
? {
...el,
myData: [...el.myData, { data_name, data_title }]
}
: el
)
);
}
};

const addNewTab = () => {
/**
* window.prompt is just a quick way to request input from user.
*/
const name = window.prompt("Enter Name");

if (name) {
setData((data) => [...data, new Data(generateId(), name)]);
}
};

return (
<div className="App row">
{data.map((li, index) => (
<div className="col-4 col-sm-4 col-md-3 col-lg-3 col-xl-3" key={index}>
<div
className={
index === active_menu
? "row dashboard_single_cont_active"
: "row dashboard_single_cont"
}
onClick={() => setactive_menu(index)}
>
<div className="dashboard_name col-10 col-sm-10 col-md-9 col-lg-10 col-xl-10">
{li.name}
</div>
<div
className={
active_menu === index
? "dashboard_option_active col-2 col-sm-2 col-md-3 col-lg-2 col-xl-2"
: "dashboard_option col-2 col-sm-2 col-md-3 col-lg-2 col-xl-2"
}
align="center"
></div>
</div>
</div>
))}

{data.length < 4 && <button onClick={addNewTab}>ADD</button>}

<div className="col-11 col-sm-11 col-md-11 col-lg-11 col-xl-11">
<div className="row">
{data[active_menu].myData.map((li, index) => (
<div
key={index}
className="col-11 col-sm-11 col-md-8 col-lg-6 col-xl-6"
>
<NEsted data={li} />
<br></br>
</div>
))}
</div>
</div>
<RightBar Create_element={Create_element} />
</div>
);
}

Dynamic Tabs with Global behaviourSubject?

I think you are missing a getter for the showActionTabs.

It is always going to set showTabs as whatever the behavior subject is initialized as.

You can try:

service globals.ts

getShowActionTabs(): Observable<boolean>{
return this.showActionTabs.asObservable();
}

tabs.ts

this._appGlobals.getShowActionTabs().subscribe((value:boolean) => { this.showTabs = value;
console.log(value);
});

Dynamically create and append tab space in Javascript

Along with this solution " "
to create space between two dynamically created elements, you can also create any element for example span also which should not be block level element. For example to create space between checkbox and label which are created dynamically

var cb = document.createElement("input");
var label = document.createElement("label");
var span = document.createElement("span");
append(li,cb);
append(li,span);
append(li,label);
span.innerHTML = " ";

Dynamically Nested Tab Creation

You need to format your code for easy to read

You used wrong param to compare data1[i] instead of data1[i][0]. That make wrong href also

You need to append all in data to dyn_div. Don't use $(.p).append, that will get the appended item instead of current item.

$(document).ready(function () {
var data1 = [
["FINANCE"],
["SALE"],
["SALE3"]
];
var data2 = [
["FINANCE", "FINANCE1"],
["FINANCE", "FINANCE2"],
["SALE", "SALE1"],
["SALE", "SALE2"],
["SALE", "SALE3"],
["SALE", "SALE4"],
["SALE3", "NOSALE"]
]
var stringData = "";
var dyn_ul = "";
var dyn_li = '';
var dyn_div = '';
for (var i = 0; i < data1.length; i++) {
stringData = $("<li><a data-toggle='tab' href=#" + data1[i][0] + ">" + data1[i][0] + "</a></li>");
$(".list").append(stringData);
$(".list li:first-child").addClass("active");
dyn_div = "<div class='tab-pane fade' id=" + data1[i][0] + "><ul class='p nav nav-tabs'>";
for (var j = 0; j < data2.length; j++) {
if (data2[j][0] === data1[i][0]) {
dyn_div += "<li><a data-toggle='tab' href=#" + data2[j][1] + ">" + data2[j][1] + "</a></li>";
}
}
dyn_div += '</ul></div>';
$(".tab-content").append(dyn_div);
}
$(".p li:first-child").addClass("active");
$(".tab-content div.tab-pane:first-child").addClass("active in");
})

Adding dynamic bootstrap tab before the existing 'addition' tab

jQuery offers prepend or before methods depending on what you really want.

prepend

<ul class="nav nav-tabs ">
<li>prepending adds element here</li>
<li></li>
<li class="plus"><a href="#" class="add-newTab">+</a></li>
</ul>

before

<ul class="nav nav-tabs ">
<li></li>
<li>before adds element here when used on $('.plus')</li>
<li class="plus"><a href="#" class="add-newTab">+</a></li>
</ul>

Here's a simplified implementation of your list and tabs:

var TabView = Backbone.View.extend({
//create <li> element to hold each tab
tagName: "li",
className: "currentTab", // why? all tabs will have "currentTab"

initialize: function() {
//creates new div for tab content
this.tabContent = new TabContentView({
model: this.model
});
},

// render should only renders, and should be idempotent.
render: function() {
this.$el.empty().append(tabContent.render().el);

// returning "this" is the default in Backbone, which enables
// chaining method calls.
return this;
}
});

var ListView = Backbone.View.extend({
//<ul> element for tabs
el: '.nav-tabs',
template: '<li class="plus"><a href="#" class="add-newTab">+</a></li>',
events: {
"click .add-newTab": "onAddTab",
},
render: function() {
this.$el.empty().append(this.template);

// cache the '+' li element.
this.$plus = this.$('.plus');
return this;
},

onAddTab: function(e) {
var tabView = new TabView({ model: this.model });

// the magic happens here.
// if you want the tab at the beginning of the ul:
this.$el.prepend(tabView.render().el);

// or if you want it at the end, but before the + :
this.$plus.before(tabView.render().el);
},
});

You don't need to use the global jQuery to select elements, Backbone views have their own element pre-scoped and cached accessible through this.$el.

If you really need to find an element inside the view's el, you can select it using this.$('.element-you-want') which is a shortcut for:

$(this.el).find('.element-you-want')

Dynamically Generate Plots in Conditional Tabs using renderUI in Shiny

Your example isn't exactly minimal so i did some stripping away. First the data and helper functions

library(shiny)
library(ggplot2)

channels = c("Affiliate","Email","Media","SEO")
nObs = c(round(runif(1,100,200)))

myData = data.frame(
Campaign = unlist(lapply(channels, FUN = function(x) paste(x,seq(from=1,to=nObs,by=1),sep=""))),
Channel = rep(channels,nObs),
Return = runif(nObs*length(channels),50,500),
Spend = runif(nObs*length(channels),10,100)
)

plotSingle = function(myData, channelName){
ggplot(myData[which(myData$Channel==channelName),], aes(x = Spend, y = Return)) +
geom_point(color="black") +
theme(panel.background = element_rect(fill = 'grey85'),
panel.grid.major = element_line(colour = "white"))
}

Now the UI

ui <- fluidPage(
headerPanel('Plot Testing'),
mainPanel(
uiOutput('mytabs'),
plotOutput('scatterPlot')
)
)

Note that we only use one plotOutput here. What we will do is just change the plot it's showing based on the currently selected tab. Here's the server code

server = function(input, output) {

rawData <- reactive({
myData
})

output$mytabs = renderUI({
if(is.null(rawData())){return ()}
channels = unique(rawData()$Channel)
myTabs = unname(Map(tabPanel, channels))
do.call(tabsetPanel, c(myTabs, id="channeltab"))
})

output$scatterPlot <- renderPlot({
if(is.null(rawData()) | is.null(input$channeltab)){return ()}
plotSingle(rawData(), input$channeltab)
})

}

You see we set an id on the tabsetPanel we create. We can then use that as input to determine which panel is selected and show the correct plot. All run with

shinyApp(ui = ui, server = server)

How to create dynamic tabSetPanels with the same selected tab that reacts to user input in R Shiny?

  • The value of a tabPanel must be a character string :

    output$set1 <- renderUI({
    tabs <- list()
    for(i in seq_len(input$tabSelector)){
    tabs[[i]] <- tabPanel(
    title = paste0("tab",i),
    value = as.character(i),
    numericInput(
    paste0("num",i),
    "Number",
    value = 0
    )
    )
    }
    do.call(tabsetPanel, c(tabs,
    list(id = "set1",
    selected = as.character(input$tabSelector))
    ))
    })
  • In output$set2 you don't need to set the value of the selected argument, because it will be set by the updateTabsetPanel.

  • Duplicated ids are not allowed in HTML, so you have to change the id paste0("num",i) of your numeric inputs to something else in one of the two tabsets.



Related Topics



Leave a reply



Submit