본문 바로가기
asp.net

[.net] mvc 웹 프로젝트(3)

by TTTGGG 2024. 6. 27.
728x90
반응형
SMALL

공통 레이아웃 구성

 - 상단 메뉴 클릭하면 우측에 사이드 메뉴 출력

 - 사이드 메뉴에서 부모 노드, 자식 노드 구성

_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');
        }
    }
});

728x90
반응형
LIST

'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