1

I'm creating a web page using .Net Core and I have a pretty complex program as of now. I have two different models (Customer and User) with different properties and they each have a controller and a view.

I want to put these two views/controllers together in a tab view, so that under the Customer tab the customer index is shown and under the User tab the user index is show. I want it to be similar to what the following generates:

<div>
    <ul class="nav nav-tabs" role="tablist">
        <li role="presentation" class="active">
            <a href="#first" aria-controls="first" role="tab" data-toggle="tab">First</a>
        </li>
        <li role="presentation">
            <a href="#second" aria-controls="second" role="tab" data-toggle="tab">Second</a>
        </li>
    </ul>

    <div class="tab-content">
        <div role="tabpanel" class="tab-pane active" id="first">
            Customer
        </div>
        <div role="tabpanel" class="tab-pane" id="second">
            User
        </div>
    </div>
</div>

Tab View

Is this possible in .Net Core?

EDIT1:

I am aware of how to use partial views and I was thinking this might be the way, however I am not an expert on how to use these. I just used them to restructure the way the details page for my models were shown. However as I have already implemented two complete controllers and their corresponding views I was wondering if there is an easier way?

Could I create a viewModel (containing a list of customers and users) and use that to display either? (I am using a PaginatedList on both views to order and limit the amount of items shown on the pages.). In the controller below I try to get the customers and users from the DB and pass them to the viewModel which is then passed to the View. Is this how it can be done in .Net Core? (Problem can be found in commented code in controller).

Model

public class CustomerUserViewModel
{
    public PaginatedList<Customer> Customers { get; set; }
    public PaginatedList<User> Users { get; set; }

    //OR

    public List<Customer> Customers { get; set; }
    public List<User> Users { get; set; }
}

View

@model CustomerUserViewModel

<h2>CustomerUserTabbing</h2>
<div>
    <ul class="nav nav-tabs" role="tablist">
        <li role="presentation" class="active">
            <a href="#first" aria-controls="first" role="tab" data-toggle="tab">Customer</a>
        </li>
        <li role="presentation">
            <a href="#second" aria-controls="second" role="tab" data-toggle="tab">User</a>
        </li>
    </ul>

    <div class="tab-content">
        <div role="tabpanel" class="tab-pane active" id="first">
            @Html.Partial("~/Views/Customer/Index.cshtml", Model.Customers)
        </div>
        <div role="tabpanel" class="tab-pane" id="second">
            @Html.Partial("~/Views/User/Index.cshtml", Model.Users)
        </div>
    </div>
</div>

Controller

public IActionResult CustomerUserTabbing()
{
    //I found that these two lines don't actually return anything
    //See new code below this
    //var userlist = from u in _context.Users select u;
    //var customerlist = from c in _context.Customers select c;

    //New Code
    var users = _context.Users
       .Where(u => u.UserID != 0)
       .OrderBy(u => u.First_Name)
       .ToList();
    var customers = _context.Customers
       .Where(c => c.CustomerID != 0)
       .OrderBy(c => c.Name)
       .ToList();

    CustomerUserViewModel viewModel = new CustomerUserViewModel();
    viewModel.Customers = customers; //these two lines gives errors
    viewModel.Users = users; //cannot convert IQueryable to PaginatedList

    return View(viewModel);
}

Does anyone know how to solve this?

Zeliax
  • 4,987
  • 10
  • 51
  • 79

2 Answers2

6

In earlier versions of ASP.NET MVC you can use @Html.Action(take a look at How can I use Html.Action?). It loooks similar to @Html.Partial(...) but instead of just returning an PartialView through passing a model it executes the Action (and you just have to return the PartialView after the Action processing). This way you can segregate Customer and Users responsabilities.

However, in ASP.NET Core this feature has been replace by ViewComponents https://docs.asp.net/en/latest/mvc/views/view-components.html

If you still want´s to use @Html.Action you can do it by implementing by yourself, take a look on how to do it at @Html.Action in Asp.Net Core (not the selected answer)

Regards.

Community
  • 1
  • 1
dime2lo
  • 826
  • 8
  • 15
  • This sounds like what I want, however when I try to use `@Html.Action` Visual Studio complains. The only method I have available seems to be `@Html.ActionLink`.. – Zeliax Oct 31 '16 at 13:26
  • Yes, you are right. It seems like that `@Html.Action` was removed from .Net Core, and you should use [ViewComponents](https://docs.asp.net/en/latest/mvc/views/view-components.html) instead. However you can implement your own `@Html.Action` [take a look here](http://stackoverflow.com/questions/26916664/html-action-in-asp-net-core) – dime2lo Oct 31 '16 at 14:29
  • Yeah. I stumpled upon that as well, and will give it a go once I have read up on the docs for View Components. It might be that View Components is what I need and since that is what they plan to go with for now it might not be a bad idea to get into :P – Zeliax Oct 31 '16 at 14:32
  • Updated the answer so that it takes both, ASP.NET MVC (`@Html.Action(...)`) and ASP.NET Core (ViewComponents), in consideration. – dime2lo Oct 31 '16 at 16:23
2

I have been using tabs for a number of years with MVC. The example provided below has 9 tabs. Each with their own Controller

@{ 
    string currentCntrlr = (string)ViewContext.RouteData.Values["controller"]; }

<nav class="container">
    <ul id="tabbedMenu">
        @if (string.IsNullOrEmpty(id)) {
            <li class="@(currentCntrlr == "Home" ? "active" : "")">@Html.ActionLink("Cover Page", "Index", "Home")</li>
        } else {
            <li class="@(currentCntrlr == "Home" ? "active" : "")">@Html.ActionLink("Cover Page", "Index2", "Home")</li>
        }
        <li class="@(currentCntrlr == "JournalIndex" ? "active" : "")">@Html.ActionLink("Index", "Index", "JournalIndex")</li>
        <li class="@(currentCntrlr == "JournalFuture" ? "active" : "")">@Html.ActionLink("Future Log", "Index", "JournalFuture")</li>
        <li class="@(currentCntrlr == "JournalMonthly" ? "active" : "")">@Html.ActionLink("Monthly Log", "Index", "JournalMonthly")</li>
        <li class="@(currentCntrlr == "JournalDaily" ? "active" : "")">@Html.ActionLink("Daily Log", "Index", "JournalDaily")</li>
        <li class="@(currentCntrlr == "JournalUser" ? "active" : "")">@Html.ActionLink("User Page", "Index", "JournalUser")</li>
        <li class="@(currentCntrlr == "JournalCalendar" ? "active" : "")">@Html.ActionLink("Calendar", "Index", "JournalCalendar")</li>
        <li class="@(currentCntrlr == "Search" ? "active" : "")">@Html.ActionLink("Search", "Index", "Search")</li>
        <li class="@(currentCntrlr == "Export" ? "active" : "")">@Html.ActionLink("Export", "Index", "Export")</li>
    </ul>
</nav>
<div class="container body-content">
    @RenderBody()
    <hr />
    <footer>
        <span class="pull-left">&copy; 2016 - LynxJournal (@ViewBag.Application)</span><span class="pull-right"><a asp-controller="Home" asp-action="Contact" title="LynxJournal Support">LynxJournal Support</a></span>
    </footer>
</div>

The active value is set in the code on the view by

@{
    string currentCntrlr = (string)ViewContext.RouteData.Values["controller"];
    string userName = (from c in User.Claims where c.Type == "name" select c.Value).FirstOrDefault();
    string id = (from c in User.Claims where c.Type == "sub" select c.Value).FirstOrDefault();
}

All of this is in the _Layout.cshtml and the @RenderBody comes from each View for your controller

PMC
  • 4,698
  • 3
  • 37
  • 57
John Davidson
  • 637
  • 5
  • 11
  • Do you know if this can be done outside of the _Layout.cshtml? And what is your `currentCntrlr`? Is that a class or a model? – Zeliax Nov 01 '16 at 08:38
  • I have added the definition of the currentCntrlr to the code sample above. This does not need to be in _Layout.cshtml directly, but that is how it gets shown on every page request, which is how my flow works - the tabs are always visible. You can change this to suite your flow. – John Davidson Nov 01 '16 at 20:06
  • This seems like the approach I want, however I would want it to show only 2 subset of pages. So let's say I for instance navigate to a certain page (called tab-page). In this page I want to show a user tab and a customer tab. In either of the tabs I want to show the respective view connected to their respective controllers. How can this be done? From what I've read I need to use a "layout" type of page but I haven't been able to find any tutorials on this. – Zeliax Nov 02 '16 at 09:04
  • @Zeliax If you only want to show 2 tabs then you only need 2 lines instead of the 8 that I have above. Then you need to one controller for each View. Then you may need a View for each command you have in the controller. A simpler way is shown in http://www.codeproject.com/Questions/1034971/Multiple-tabs-in-MVC – John Davidson Nov 02 '16 at 18:03