공통 레이아웃 구성
- 상단 메뉴 클릭하면 우측에 사이드 메뉴 출력
- 사이드 메뉴에서 부모 노드, 자식 노드 구성
_Layout.cshtml
<header>
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<a class="navbar-brand" href="/">MyWebApp</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav me-auto mb-2 mb-md-0" id="dynamicMenu">
<!-- 동적으로 생성될 메뉴 -->
</ul>
<ul class="navbar-nav ml-auto">
@if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<a class="nav-link" href="/Account/Logout">Logout</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link" href="/Account/Login">Login</a>
</li>
}
</ul>
</div>
</nav>
</header>
<div class="container-fluid">
<div class="row">
<nav id="sidebar" class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="position-sticky" style="top: 0;">
<ul class="nav flex-column" id="sidebarMenu">
<!-- 동적으로 채워질 소분류 메뉴 -->
</ul>
</div>
</nav>
<main role="main" class="col-md-10 ms-sm-auto px-md-4 main-content">
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="home-tab" data-bs-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div id="mainContent">
@RenderBody()
</div>
</div>
</div>
</main>
</div>
</div>
<footer class="footer">
<div class="container">
<span class="text-muted">Place footer content here.</span>
</div>
</footer>
등록한 메뉴 데이터 가져올 수 있게 MenuController 구성
[HttpGet]
public IActionResult GetMenu()
{
var menus = _context.Menus
.Include(m => m.SubMenus)
.ThenInclude(sm => sm.SubMenus)
.ToList();
var menuDtos = menus.Select(m => new
{
m.Name,
m.Category,
SubMenus = http://m.SubMenus.Select(sm => new
{
sm.Name,
sm.Href,
SubMenus = shttp://m.SubMenus.Select(sub => new { sub.Name, sub.Href }).ToList()
}).ToList()
}).ToList();
return Ok(menuDtos);
}
상단 메뉴, 사이드 메뉴 동적으로 생성되도록 site.js 수정
document.addEventListener("DOMContentLoaded", function () {
console.log("DOMContentLoaded event fired");
var tabs = document.getElementById("myTabContent");
var tabList = document.getElementById("myTab");
var mainContent = document.getElementById("mainContent");
var sidebarMenu = {};
function activateLastTab() {
var tabs = tabList.querySelectorAll('.nav-link');
var lastTab;
for (var i = tabs.length - 1; i >= 0; i--) {
if (!tabs[i].classList.contains('close')) {
lastTab = tabs[i];
break;
}
}
if (lastTab) {
var bsTab = new bootstrap.Tab(lastTab);
bsTab.show();
} else if (mainContent) {
mainContent.style.display = 'block';
}
}
fetch('/api/menu')
.then(response => response.json())
.then(data => {
const menuContainer = document.getElementById('dynamicMenu');
data.forEach(item => {
const li = document.createElement('li');
li.className = 'nav-item';
const a = document.createElement('a');
a.className = 'nav-link';
a.href = '#';
a.setAttribute('data-category', item.category);
a.textContent = item.name;
li.appendChild(a);
menuContainer.appendChild(li);
sidebarMenu[item.category] = item.subMenus || [];
a.addEventListener('click', function (e) {
e.preventDefault();
var category = this.getAttribute('data-category');
var menuItems = sidebarMenu[category];
if (!menuItems || !Array.isArray(menuItems) || menuItems.length === 0) {
console.error(`Menu items for category "${category}" are not defined or not an array.`);
return;
}
var sidebar = document.getElementById('sidebarMenu');
sidebar.innerHTML = '';
menuItems.forEach(function (item) {
var li = createSidebarMenuItem(item);
sidebar.appendChild(li);
});
});
});
})
.catch(error => {
console.error("Error fetching menu data:", error);
});
function createSidebarMenuItem(item) {
var li = document.createElement('li');
li.className = 'nav-item';
if (item.subMenus && Array.isArray(item.subMenus) && itehttp://m.subMenus.length > 0) {
var a = document.createElement('a');
a.className = 'nav-link parent-node';
a.style.cursor = 'pointer';
a.textContent = item.name;
li.appendChild(a);
var ul = document.createElement('ul');
ul.className = 'nav flex-column sub-menu';
ul.style.display = 'none';
item.subMenus.forEach(function (child) {
var childLi = createSidebarMenuItem(child);
ul.appendChild(childLi);
});
li.appendChild(ul);
a.addEventListener('click', function (e) {
e.preventDefault();
ul.style.display = ul.style.display === 'none' ? 'block' : 'none';
a.classList.toggle('expanded');
});
} else {
var a = document.createElement('a');
a.className = 'nav-link';
a.href = item.href;
a.textContent = item.name;
li.appendChild(a);
a.addEventListener('click', function (e) {
e.preventDefault();
handleMenuClick(a);
});
}
return li;
}
function handleMenuClick(element) {
var tabId = element.textContent.replace(/\s+/g, '').toLowerCase();
var existingTabLink = document.getElementById(tabId + '-tab');
var existingTabContent = document.getElementById(tabId);
if (!existingTabLink) {
if (mainContent) {
mainContent.style.display = 'none';
}
document.querySelectorAll('.nav-link').forEach(function (navLink) {
navLink.classList.remove('active');
});
document.querySelectorAll('.tab-pane').forEach(function (tabPane) {
tabPane.classList.remove('show', 'active');
});
var newTabLink = document.createElement('a');
newTabLink.className = 'nav-link active';
newTabLink.id = tabId + '-tab';
newTabLink.setAttribute('data-bs-toggle', 'tab');
newTabLink.href = '#' + tabId;
newTabLink.role = 'tab';
newTabLink.setAttribute('aria-controls', tabId);
newTabLink.innerHTML = element.textContent + ' <button type="button" class="btn-close" aria-label="Close"></button>';
var newTabItem = document.createElement('li');
newTabItem.className = 'nav-item';
newTabItem.appendChild(newTabLink);
tabList.appendChild(newTabItem);
var newTabContent = document.createElement('div');
newTabContent.className = 'tab-pane fade show active';
newTabContent.id = tabId;
newTabContent.role = 'tabpanel';
newTabContent.setAttribute('aria-labelledby', tabId + '-tab');
newTabContent.innerHTML = '<div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading...</span></div>';
tabs.appendChild(newTabContent);
fetch(element.href)
.then(response => response.text())
.then(data => {
newTabContent.innerHTML = data;
console.log(element.href);
// 동적으로 로드된 콘텐츠 내의 스크립트를 실행
var scripts = newTabContent.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
var scriptContent = scripts[i].innerHTML;
// 새로운 스크립트 태그 생성 및 즉시 실행
var script = document.createElement('script');
script.type = 'text/javascript';
script.textContent = "(function() {" + scriptContent + "})();";
// 디버깅을 위한 로그 추가
console.log("Adding wrapped script:", script.textContent);
// 스크립트 태그를 DOM에 추가하여 스크립트 실행
document.body.appendChild(script);
document.body.removeChild(script);
}
// 탭을 활성화
var bsTab = new bootstrap.Tab(newTabLink);
bsTab.show();
})
.catch(error => {
console.error("Error loading tab content:", error);
});
newTabLink.querySelector('.btn-close').addEventListener('click', function (e) {
e.stopPropagation();
var tabPane = document.getElementById(tabId);
var tabItem = newTabLink.parentNode;
var wasActive = newTabLink.classList.contains('active');
tabPane.parentNode.removeChild(tabPane);
tabItem.parentNode.removeChild(tabItem);
if (wasActive) {
activateLastTab();
}
if (document.querySelectorAll('.tab-pane.show.active').length === 0 && mainContent) {
mainContent.style.display = 'block';
}
});
} else {
document.querySelectorAll('.nav-link').forEach(function (navLink) {
navLink.classList.remove('active');
});
document.querySelectorAll('.tab-pane').forEach(function (tabPane) {
tabPane.classList.remove('show', 'active');
});
if (mainContent) {
mainContent.style.display = 'none';
}
var bsTab = new bootstrap.Tab(existingTabLink);
bsTab.show();
existingTabContent.classList.add('show', 'active');
}
}
});
'asp.net' 카테고리의 다른 글
[.net] LINQ (0) | 2024.07.05 |
---|---|
[.net] mvc 웹 프로젝트(5) (0) | 2024.06.27 |
[.net] mvc 웹 프로젝트(4) (0) | 2024.06.27 |
[.net] mvc 웹 프로젝트(2) (0) | 2024.06.27 |
[.net] mvc 웹 프로젝트(1) (0) | 2024.06.27 |