Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asp.net Core View Component executed but markup not rendered

I have an ASP.NET Core 1.1 web application developed with VS.2017 and I decided to put some of the view functionality in a view component (have done others before).

This view component fetches a Dictionary collection of permissions associated to a user ID and displays them in a nice table. When I put it as part of the page (not VC) it works. But when I use it as a view component the component's HTML is never rendered.

I placed a breakpoint in the view component and it triggers, I see the View(granted) return statement return a populated list so up until there execution is as expected.

Then I placed a breakpoint in the ViewComponent's default.cshtml code section at the top the @{ some code here } and that breakpoint triggers as well, so the view component's Default.cshtml file is found. This Default.cshtml has some markup to render a table, therein within the table I have a @foreach() statement and when do a "Run to cursor" to that precise location -the loop that iterates- it triggers as well so it is iterating through the collection.

But after all that the main view looks as if the view component isn't there, none of the HTML found in the Default.cshtml is rendered even though it was found and executed. What am I missing here? so far my impression has been that VS.2017 (with all its updates) is not very stable.

Default.cshtml

@using ACME.AspNetCore.Permissions.Infrastructure.Authorization
@model Dictionary<Permission, string>
@{ 
    ViewBag.UserName = "xxx";
    Model.Add(Permission.Permission1, "test");
}

<h1>Component Output</h1>
<div class="well well-lg">
<table class="table table-hover">
    <thead>
        <tr>
            <th>Permission</th>
            <th>Description</th>
            <th class="text-center">Status</th>
            <th class="text-center">Revoke it!</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var dictentry in Model)
        {
            <tr>
                <td>@dictentry.Key.ToString()</td>
                <td>@dictentry.Value</td>
                <td class="text-center"><span class="glyphicon glyphicon-ok" style="color:green;"></span></td>
                <td class="text-center"><a asp-action="RevokePermission" asp-route-id="@ViewBag.UserName" asp-route-pid="@dictentry.Key.ToString()"><span class="glyphicon glyphicon-thumbs-down" style="color:red;"></span></a></td>
            </tr>
        }
    </tbody>
    <tfoot><p class="alert alert-success"><span class="glyphicon glyphicon-eye-open"></span> Granted permissions</p></tfoot>
</table>
</div>

GrantedPermissionsViewComponent.cs

 [ViewComponent(Name = "GrantedPermissions")]
 public class GrantedPermissionsViewComponent : ViewComponent {
     private readonly ApplicationDbContext _context;
     public GrantedPermissionsViewComponent(ApplicationDbContext context) : base()
    {
        _context = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(string emailOrUserId)
    {
        string id;
        Guid UID;
        if (Guid.TryParse(emailOrUserId, out UID))
        {   // THE PARAMETER WAS A GUID, THUS A USER ID FROM IDENTITY DATABASE
            id = emailOrUserId;
        }
        else
        {   // THE PARAMETER IS ASSUMED TO BE AN EMAIL/USERNAME FROM WHICH WE CAN DERIVE THE USER ID
            id = _context.Users.Where(u => u.Email == emailOrUserId.Trim()).Select(s => s.Id).FirstOrDefault();
        }

        Dictionary<Permission, string> granted = GetOwnerPermissions(id);
        return View(granted);            
    }

    private Dictionary<Permission, string> GetOwnerPermissions(string userId) 
    {
        Dictionary<Permission, string> granted;
        granted = _context.UserPermissions.Where(u => u.ApplicationUserId == userId)
                                                .Select(t => new { t.Permission })
                                                .AsEnumerable() // to clients memory
                                                .Select(o => new KeyValuePair<Permission, string>(o.Permission, o.Permission.Description()))
                                                .ToList()
                                                .ToDictionary(x => x.Key, x => x.Value);

        return granted;
    }
 }

So why on earth is it triggering on the component's code as well as on the component's view (default.cshtml) and yet it does not render the HTML code found therein?

Component invokation in the main view:

@{await Component.InvokeAsync<GrantedPermissionsViewComponent>(
    new { emailOrUserId = ViewBag.UserName });
}

NOTE

  • The InvokeAsync is actually executing synchronously (per warning) because I could not find a way to have GetOwnerPermissions to await on anything... But that is not the problem.
like image 469
Lord of Scripts Avatar asked Jun 29 '17 19:06

Lord of Scripts


People also ask

What is a viewcomponent in ASP NET Core?

In this article, you will learn about ViewComponent in ASP.NET Core. What is a ViewComponent? ViewComponent was introduced in ASP.NET Core MVC. It can do everything that a partial view can and can do even more.

What is ASP NET Core?

ASP.NET Core is a cross-platform, high-performance, open-source framework for building modern web, cloud and internet based applications. Also, we can develop Web APIs, mobile apps and client site single page applications using our favourite development tools on Windows, macOS and Linux.

How to display data dynamically in MVC with ASP NET Core?

Create a new ASP.NET Core MVC application and run that application. You can see an empty page like the following, We have to display data dynamically. In real scenarios dynamic data mostly comes from the database but for our application we do not use a database and we will create a class to generate fome data.

What is the use of view component?

ViewComponents are completely self-contained objects that consistently render html from a razor view. ViewComponents are very powerful UI building blocks of the areas of application which are not directly accessible for controller actions. Let's suppose we have a page of social icons and we display icons dynamically.


2 Answers

The problem lies in how you are invoking the ViewComponent.

If you use @{ ... } it means you want to execute code and not render to output.

If you use parenthesis instead of brackets, the result gets rendered to output. @( ... )

In your case, you don't even need the parenthesis. Try invoking it has following:

@await Component.InvokeAsync("GrantedPermissions", new { emailOrUserId = ViewBag.UserName })

More info here

like image 80
João Pereira Avatar answered Oct 26 '22 02:10

João Pereira


try this, your mileage might vary. :)

@if (User.Identity.IsAuthenticated)
{

    <div>User: @User.Identity.Name</div>
    @(await Component.InvokeAsync("DefaultNavbar"));

    @RenderBody()
}
else
{
    <div> show public pages</div>
    @RenderBody()
}
like image 33
caydev2010 Avatar answered Oct 26 '22 03:10

caydev2010