Asp.net Core View Component completed but markup not showing

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

This view component retrieves a collection of permission vocabularies associated with a user ID and displays them in a nice table. When I put it as part of a page (not VC) it works. But when I use it as a view component, the HTML component is never displayed.

I put a breakpoint on the view component and it starts, I can see that the return statement (provided) return returns the populated list so until the execution is as expected.

Then I put a breakpoint in the code section of the ViewComponent default.cshtml at the top of @ {some code here} and that breakpoint function is also caught, so the Default.cshtml component is found. This Default.cshtml has some markup for rendering the table, it has an @foreach () statement in the table, and when you run "Run to cursor" to that exact location - a loop that iterates - it runs just like iterating through the collection.

But the main view looks like the view component does not exist, none of the HTML found in the Default.cshtml file is displayed, even if it is found and executed. What am I missing here? So far my impression was 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 does it run the component code as well as the component view (default.cshtml) and yet it doesn't render the HTML found in it?

Component mapping on the home screen:

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

      

Note

  • InvokeAsync actually executes synchronously (per warning) because I couldn't find a way to make GetOwnerPermissions wait for anything ... But that's not a problem.
+3


source to share


2 answers


The problem is how you call the ViewComponent.

If you use @{ ... }

it means that you want to execute the code and not output to output.

If you use parentheses instead of parentheses, the result is printed to the output. @( ... )



In your case, you don't even need parentheses. Try calling it:

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

      

More details here

+6


source


try it, your mileage may vary. :)



@if (User.Identity.IsAuthenticated)
{

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

    @RenderBody()
}
else
{
    <div> show public pages</div>
    @RenderBody()
}

      

0


source







All Articles