Telerik Forums
UI for ASP.NET Core Forum
1 answer
85 views

I have a form split into sections, and within one of the sections I have a TextArea, which I want to render as the full width of the form - even when I suppress the label text, the container and spacing is still rendered and so I lose a chunk of space to the left of the text area (horizantal form layout)

My form code :
@(
Html.Kendo().Form<Customer>()
        .Name("detailForm")
        .HtmlAttributes(new { action = @Url.Action("EditCustomer", "Customer"), method = "POST" })
        .Orientation("horizontal")
        .Items(items =>
        {
            items.AddGroup()
            .Label("Information")
            .Layout("grid")
            .Grid(g => g.Cols(2).Gutter(10))
                .Items(i =>
                {
                    i.Add()
                        .Field(f => f.CompanyName)
                        .Label(l => l.Text("Company Name:").Optional(false));
                    i.Add()
                        .Field(f => f.CustomerStatusId)
                        .Label(l => l.Text("Status:").Optional(false))
                        .Editor(e =>
                        {
                            e.DropDownList()
                                .HtmlAttributes(new { })
                                .DataTextField("KeyDescription")
                                .DataValueField("KeyValue")
                                .HtmlAttributes(new { style = "width:100%" })
                                .Size(ComponentSize.Small)
                                .FillMode(FillMode.Outline)
                                .DataSource(source =>
                                {
                                    source.Read(read =>
                                    {
                                        read.Action("LookupValues", "Lookup", new { type = "CustomerStatus" });
                                    })
                                    .ServerFiltering(true);
                                });
                        });
                });
            items.AddGroup()
            .Label("Description")
                .Items(i =>
                {
                    i.Add()
                        .Field(f => f.Description)                        
                        .Label(l => l.Text(" "))
                        .Editor(e =>
                        {
                            e.TextArea()
                                .Size(ComponentSize.Small)
                                .Overflow(TextAreaOverflow.Scroll)
                                .Rows(8);
                        });
                });

        })
        )

I have tried using the EditorTemplateHandler but that still seems to render the label section?

Thanks

 

 

 

Ivaylo
Telerik team
 answered on 05 Jul 2024
1 answer
123 views

I have set up a form component and specified an action - however when debugging I can see that the form seems not to be posting to the action I have specified.

This is the form code :

@(
Html.Kendo().Form<Customer>()
        .Name("portalForm")
        .HtmlAttributes(new { action = "EditPortal", method = "POST" })
        .Items(items =>
        {
            items.Add()
                .Field(f => f.CustomerId)
                .Editor(editor => editor.Hidden());
            items.Add()
                .Field(f => f.Customer_Guid)
                .Editor(editor => editor.Hidden());
            items.Add()
                .Field(f => f.AppId)
                .Editor(editor => editor.Hidden());
            items.AddGroup()
            .Label("Settings")
            .Layout("grid")
            .Grid(g => g.Cols(4).Gutter(20))
                .Items(i =>
                {
                    i.Add()
                        .Field(f => f.CostCentresActive)
                        .Label(l => l.Text("CostCentresActive:").Optional(true))
                        .Editor(e =>
                        {
                            e.Switch()
                                .Messages(c => c.Checked("YES").Unchecked("NO"));
                        });
                    i.Add()
                        .Field(f => f.CostCentreHide)
                        .Label(l => l.Text("CostCentreHide:").Optional(true)).Editor(e =>
                        {
                            e.Switch()
                                .Messages(c => c.Checked("YES").Unchecked("NO"));
                        });
                    i.Add()
                        .Field(f => f.CustomTextActive)
                        .Label(l => l.Text("CustomTextActive:").Optional(true)).Editor(e =>
                        {
                            e.Switch()
                                .Messages(c => c.Checked("YES").Unchecked("NO"));
                        });
                    i.Add()
                        .Field(f => f.DepartmentsActive)
                        .Label(l => l.Text("DepartmentsActive:").Optional(true)).Editor(e =>
                        {
                            e.CheckBox();
                           });
                });
        })
        )

The rendered html seems to suggest it will post to the EditPortal action, but when I click submit it posts to the Edit action which is what rendered the page with the form on in the first place. 

It is a little complicated by the fact that my form component is inside a partial view, which in turn is in a tabstrip on the main Edit view. 

I am probably overlooking something very simple here!

 

Thanks

 

 

 

Chris
Top achievements
Rank 1
Iron
Iron
Iron
 answered on 28 Jun 2024
1 answer
92 views

If I place a simple Kendo Button in the partial page it shows fine, but the button group does not show at all.  The developer tools and debugging shows everything to be correct and the partial view is being returned via return PartialView(partialViewName); from the controller.

This is driving me crazy.  Is this a known issue?  There are no errors in the console/developer tools.  It is specific to the Kendo Button GROUP.

Please help...thanks!

The view that contains the partial view:

@using Kendo.Mvc.UI

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div style="max-width:45rem;">
    <div>Administrative Areas</div>
    <div id="buttonGroup">
        @(Html.Kendo().ButtonGroup()
            .Name("adminButtonGroup")
            .Items(t =>
            {
                t.Add().Text("Personal").HtmlAttributes(new { onclick = "loadPartial('_Admin')" });
                t.Add().Text("Predefined").HtmlAttributes(new { onclick = "loadPartial('_Admin')" });
                t.Add().Text("Regions").HtmlAttributes(new { onclick = "loadPartial('_Admin')" });
                t.Add().Text("Flags").HtmlAttributes(new { onclick = "loadPartial('_Admin')" });
            })
            )
    </div>
</div>

<div id="adminPartial" class="mt-2">

</div>

<script>

    $(function () {
        // Set the first button as active by default
        $("#adminButtonGroup").data("kendoButtonGroup").select(0);

    });

    function loadPartial(partialViewName) { //clickedElement
        var targetDiv = document.getElementById("adminPartial");

        console.log(targetDiv.innerHTML);

        fetch('/Home/LoadPartialView?partialViewName=' + partialViewName)
            .then(response => response.text())
            .then(html => {
                targetDiv.innerHTML = html;
                console.log("partial view html:" + html);
            })
            .catch(error => {
                console.log('Error:', error);
            });
    }
</script>

 

Contents of the _Admin partial View: (does not display):

@using Kendo.Mvc.UI

<div>
@(Html.Kendo().ButtonGroup()
            .Name("adminPartialButtonGroup")
            .Items(t =>
            {
                t.Add().Text("Test 1");
                t.Add().Text("Test 2");
                t.Add().Text("Test 3");
                t.Add().Text("Test 4");
            })
            )
</div>

<script>

Home Controller Action:

 public IActionResult LoadPartialView(string partialViewName)
 {
     if (string.IsNullOrEmpty(partialViewName))
     {
         return BadRequest();
     }

     return PartialView(partialViewName);
 }

 

Tsvetomila
Telerik team
 answered on 28 Jun 2024
0 answers
70 views

Hello,

We are having scrolling issues with the fairly large menu and sub menu items. Instead of scrolling child menu items on selection, it scrolls the main menu options.

Please advise.

 function onChangesideMenuSwitch(e) {
     var menuContainer = $("#menuContainer");
     var mainSection = $("#mainSection");
     if (!e.checked) {
             menuContainer.hide();
             menuContainer.css("width", "0");
             menuContainer.css("z-index", "-1");
             mainSection.css("padding-left", "0");
         }
 else {
             menuContainer.show();
             menuContainer.css("width", "250px");
             menuContainer.css("z-index", "3");
             mainSection.css("padding-left", "250px");
         }
         }

@model Models.Menu.TopMenuModel
@{
    string pathBase = (string)ViewBag.PathBase;
}
    <style>
        #menuContainer {
            width: 250px;
            overflow: visible;
            display: block;
            z-index: 3;
            height: calc(100% - 100px);
            background-color: white;
            float: left;
            flex: none;
            position: fixed;
            top: 100px;
            margin-bottom: 10px;
        }

        .menuSeperator {
            padding: 2px;
            background-color: var(--cwru-blue);
            border-style: solid;
            border-width: 1px;
        }


        .menuSeperator > .k-menu-link-text {
            font-style: italic;
            color: whitesmoke;
            white-space: normal;
        }

        .menuItem {
            font-style: italic;
            color: whitesmoke;
            background-color: var(--cwru-blue);
        }

        .menuExpandable {
            color: var(--cwru-blue);
            white-space: normal;
            min-width: 200px;
        }

        .k-menu, .k-menu-link-text {
            font-size: 16px;
            color: black;
            padding: 2px;
        }
    </style>


<script type="text/javascript">
    $(document).ready(function () {
        $("#leftMenu").height(window.innerHeight - 120)
        $(window).resize(function () {
            $("#leftMenu").height(window.innerHeight - 120)
        });
    });
</script>

<div id="menuContainer">
    @(Html.Kendo().Menu()
                .Name("leftMenu")
                .HoverDelay(200)
                .Orientation(MenuOrientation.Vertical)
                .Direction(MenuDirection.Right)
                .Scrollable(true)
                .Items(items =>
                {
                    foreach (var menuEntryGroup in Model.AllowedPages.Where(ap => ap.parent_id == null).GroupBy(ap => ap.menucategory_id))
                    {
                        //menu category
                        items.Add().Text(menuEntryGroup.First().menucategory.name)
                            .LinkHtmlAttributes(new { @class = "menuSeperator" }).HtmlAttributes(new { @class = "menuItem" });

                        foreach (var menuEntry in menuEntryGroup)
                        {
                            //top level items.
                            items.Add().Text(menuEntry.name).Url((pathBase == "/" ? "" : pathBase) + "/" + menuEntry.controller + "/" + menuEntry.Viewer)
                                .Items(subitem =>
                                {
                                    foreach (var subentry in Model.AllowedPages.Where(ap => ap.parent_id == menuEntry.id))
                                    {
                                        //second level items
                                        subitem.Add().Text(subentry.name).Url((pathBase == "/" ? "" : pathBase) + "/" + subentry.controller + "/" + subentry.Viewer).LinkHtmlAttributes(new { @class = "menuExpandable" })
                                            .Items(subsubitem =>
                                            {
                                                foreach (var subsubentry in Model.AllowedPages.Where(ap => ap.parent_id == subentry.id))
                                                {
                                                    //third level items
                                                    subsubitem.Add().Text(subsubentry.name).Url((pathBase == "/" ? "" : pathBase) + "/" + subsubentry.controller + "/" + subsubentry.Viewer);
                                                }
                                            });
                                    }
                                });
                        }

                    }
                })
        )
</div>

                        
Anita
Top achievements
Rank 1
Iron
 asked on 24 Jun 2024
1 answer
98 views

We are looking to display total number in donut chart.

Is it possible?

Number display in the center should display total when we do not mouse hover over any section of donut.

But if hover over any section, it should show that particular number in center. Look at image below.

Anton Mironov
Telerik team
 answered on 20 Jun 2024
1 answer
92 views

Hi,

I am quite new in working with Telerik and the current project that I am working is an MVC project that uses Telerik UI components for the frontend.

What I am aiming for is having several Views with each of them implementing a grid component. In the grid I have imported a list of items that can each be edited and deleted. The editing functionality uses the pop up component. However, the client is requesting that when the edit pop up opens the process of editing would be divided into two different windows/tabs meaning that in the pop up the user could navigate back and forth. That is because the entities that are being editing are large so for a more user friendly solution the client wants to divide the editing process.

So far I have not yet come across a straight forward solution to implement something like this. When in comes to implementation for a simple one tab Pop up it seems simple however if I want to introduce several tab solution, it seems to be easier to just go pure javaScript way as I am struggling mixing Telerik and my own scripts. 

I hope I making my issue clear! I would like apologize if this seems like non-problem for the pros, cause unfortunately I am not one...

Regards

Juste

Mihaela
Telerik team
 answered on 20 Jun 2024
1 answer
154 views

Hi,

Were looking into using the PDF viewer and wonder whether it's possible to render a PDF server-side? I understand that using pdf.js is obviously rendering client-side, but can you tell me if I use the document processing library whether it does indeed render server side?

If it does can you tell me if this scenario is supported in .Net 8.0?

I'm a bit confused because in the PDF viewer documentation here: https://docs.telerik.com/aspnet-core/html-helpers/pdf/pdfviewer/dpl-processing it says "To use DPL Processing in a project, it must target 4.6.2 .NET Framework", but in the DPL documentation here: https://docs.telerik.com/devtools/document-processing/introduction it says "The Telerik Document Processing libraries are available in .NET Framework and .NET Standard (.NET Core) compatible versions"

So is the .Net 4.6.2 limitation just for PDF viewer or is the documentation just out of date?

Thanks!

 

Eyup
Telerik team
 answered on 13 Jun 2024
1 answer
90 views

I had recently had an issue with Kendo Grid's export to excel feature not working and it was correctly resolved in this forum post:
https://www.telerik.com/forums/kendo-grid-excel-export-crashing-widget

I am now experiencing an issue that appears to be distinct and unrelated to the above problem on a different view within my project.

I have a Grid where I want to export the entire contents of the grid (both hidden and unhidden columns) to an excel spreadsheet.  When I click my "Export to Excel" button the following happens:

  • The "Exporting" animation shows up and hangs

  • I receive a console error indicating that the export request returned a status 405, I can also see this in the network tab

  • The breakpoint I set in my ExcelExportSave() method in my HomeController is not being hit.

What could be causing this? 

I have already implemented the fix suggested in the above linked post to import that I add JSZip directly to my layout:

<script src="https://unpkg.com/jszip/dist/jszip.min.js"></script>

 

Additional Context

The Export to Excel will fail even if I attempt to export the contents of the grid before any lines are added.

This is a read-only grid that populates 1 to N lines based on an Ajax call.  Due to the way I need this to function for my users I did not use the dataSource CRUD operations.  Rather, I use external JavaScript to directly edit the table contents.  This could be a possible source of the problem, but I do not know how I would fix.

There are two ways to add lines to the table, the first is to paste in rows and wait for the Ajax call.  The second is to add them using an input field above the grid. 

The Code

Here's the table and the relevant JavaScript that populates it:

Html.Kendo().Grid<UPH.ADCommander.Models.ExtendedUserDisplayObject>()
.Name("Grid")
.Columns(columns =>
{
    columns.Bound(c => c.SamAccountName).Title("SamAccount").Width(110).HeaderHtmlAttributes(new { style = "font-weight: bold" }).EditorTemplateName("SamAccountNameEditor");
    columns.Bound(c => c.GivenName).Title("First Name").HeaderHtmlAttributes(new { style = "font-weight: bold" });
    columns.Bound(c => c.MiddleInitial).Title("MI").Width(50).HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.Surname).Title("Last Name").HeaderHtmlAttributes(new { style = "font-weight: bold" });
    columns.Bound(c => c.Title).Title("Title").HeaderHtmlAttributes(new { style = "font-weight: bold" });
    columns.Bound(c => c.Manager).Title("Manager").HeaderHtmlAttributes(new { style = "font-weight: bold" });
    columns.Bound(c => c.ExtendedAttribute2).Title("Ex. Attr 2").HeaderHtmlAttributes(new { style = "font-weight: bold" });
    columns.Bound(c => c.ExtendedAttribute3).Title("Ex. Attr 3").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.ExtendedAttribute4).Title("Ex. Attr 4").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.ExtendedAttribute5).Title("Ex. Attr 5").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.ExtendedAttribute6).Title("Ex. Attr 6").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.ExtendedAttribute7).Title("Ex. Attr 7").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.ExtendedAttribute9).Title("Ex. Attr 9").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.ExtendedAttribute12).Title("Ex. Attr 11").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    columns.Bound(c => c.ExtendedAttribute13).Title("Ex. Attr 12").HeaderHtmlAttributes(new { style = "font-weight: bold" }).Hidden(true);
    // test columns, add more once PoC is getting the necessary data
})
.Sortable()
.Selectable(selectable => selectable
    .Mode(GridSelectionMode.Multiple)
    .Type(GridSelectionType.Cell)
)
.Navigatable()
.Scrollable()
.AllowCopy(true)
.ToolBar(tools =>
{
    tools.Custom().Text("Copy to Clipboard").HtmlAttributes(new { id = "copyButton" });
    tools.Excel();
    tools.Custom().Text("Clear").HtmlAttributes(new { id = "clearButton" });
    tools.Search();
})
.AutoBind(false)
.DataSource(dataSource => dataSource
    .Ajax() // no actual Ajax call is made with this, this is solely to define a primary key for the table so we can use the dataSource API
    .Update("GetUserDetail", "UserLookup")
    .Model(model =>
    {
        model.Id(p => p.SamAccountName); // Set SamAccountName as the ID field
    })
)
.Excel(excel => excel
    .FileName("UserDetails_" + DateTime.Now.Date.ToShortDateString() + ".xlsx")
    .Filterable(true)
    .ProxyURL(Url.Action("ExcelExportSave", "Home"))
)

                       

    function initializeGridPasteHandler(gridSelector) {
        $(gridSelector).on('contextmenu', function (e) {
            if ($(e.target).is(".k-link, .k-grid-toolbar, .k-grid-pager")) {
                return;
            }

            var offset = $(this).find("table").offset();
            var textarea = $("<textarea>");
            textarea.css({
                position: 'absolute',
                opacity: 0,
                top: offset.top,
                left: offset.left,
                border: 'none',
                width: $(this).find("table").width(),
                height: $(this).find(".k-grid-content").height()
            })
                .appendTo('body')
                .on("click", function (e) {
                    textarea.remove();
                    $(document.elementFromPoint(e.clientX, e.clientY)).click();
                })
                .on('paste', function () {
                    setTimeout(function () {
                        var value = $.trim(textarea.val());
                        var grid = $(gridSelector).data("kendoGrid");
                        var rows = value.split('\n');

                        var newRows = addRowsToGrid(rows);

                        sendUserDetailsToController(newRows);

                        textarea.remove();
                    });
                }).focus();

            return false;
        });
    }

 

function addRowsToGrid(rows) {
    console.log("addRowsToGrid fired");
    var grid = $("#Grid").data("kendoGrid");
    var existingData = grid.dataSource.data().toJSON();

    var newData = rows.map(function (row) {
        return { SamAccountName: row.trim() };
    });

    // Append new data to the existing data
    var combinedData = existingData.concat(newData);

    // Update the grid with the combined data
    grid.dataSource.data(combinedData);

    return newData;
}

 

// send the controller a list of samAccount names to get the user detail information
function sendUserDetailsToController(newRows) {
    console.log("sendUserDetailsToController fired");

    // Show the loading indicator
    $("#gridLoader").show();

    var samAccountNames = newRows.map(function (row) {
        return row.SamAccountName;
    });

    // Make the AJAX call with the new samAccountNames only
    $.ajax({
        type: "POST",
        url: "/UserLookup/GetUserDetails",
        contentType: "application/json",
        data: JSON.stringify(samAccountNames),
        success: function (response) {
            console.log("Data sent to the controller successfully.");
            updateGridWithUserDetails(response); // Update the grid with the response data
        },
        error: function (xhr, status, error) {
            console.log("An error occurred while sending data to the controller.");
        },
        complete: function () {
            $("#gridLoader").hide();; // close loading animation
        }
    });
}

 

function updateGridWithUserDetails(userDetails) {
    console.log("updateGridWithUserDetails fired");
    var grid = $("#Grid").data("kendoGrid");
    var dataSource = grid.dataSource;
    var data = dataSource.data();

    userDetails.forEach(function (userDetail) {
        var matchedRow = data.find(function (row) {
            console.log("user: " + userDetail.samAccountName);
            return row.SamAccountName.toLowerCase() === userDetail.samAccountName.toLowerCase();
        });

        console.log("matched row:" +matchedRow.SamAccountName)
        // Necessary in case of a case mismatch, e.g. SSS401 will match sss401 on the backed if you don't do this you'll get a fun bonus row added.
        matchedRow.SamAccountName = userDetail.samAccountName;

        if (matchedRow) {
            console.log("matched row:" + matchedRow.SamAccountName);

            // Update the SamAccountName to the correctly cased value
            matchedRow.set("SamAccountName", userDetail.samAccountName);

            // Update the rest of the row with the user details
            matchedRow.set("GivenName", userDetail.givenName);
            matchedRow.set("MiddleInitial", userDetail.middleInitial);
            matchedRow.set("Surname", userDetail.surname);
            matchedRow.set("Title", userDetail.title);
            matchedRow.set("Manager", userDetail.manager);
            matchedRow.set("ExtendedAttribute2", userDetail.extendedAttribute2);
            matchedRow.set("ExtendedAttribute3", userDetail.extendedAttribute3);
            matchedRow.set("ExtendedAttribute4", userDetail.extendedAttribute4);
            matchedRow.set("ExtendedAttribute5", userDetail.extendedAttribute5);
            matchedRow.set("ExtendedAttribute6", userDetail.extendedAttribute6);
            matchedRow.set("ExtendedAttribute7", userDetail.extendedAttribute7);
            matchedRow.set("ExtendedAttribute9", userDetail.extendedAttribute9);
            matchedRow.set("ExtendedAttribute12", userDetail.extendedAttribute12);
            matchedRow.set("ExtendedAttribute13", userDetail.extendedAttribute13);
        } 
    });

    // Refresh the grid to display the updated data
    grid.refresh();
}

 

function onAddRowClick(){
    var samAccountName = $("#searchInput").val().trim();

    // Check if the input is not empty
    if (samAccountName === "") {
        alert("Please enter a SamAccount name.");
        return;
    }
    var newRow = { SamAccountName: samAccountName };
    var newRows = addRowsToGrid([samAccountName]);

    // send to the Ajax call
    sendUserDetailsToController(newRows);
}

Note that # gridLoader is just my poor attempt at making a waiting animation.  It references this .css class.  This should be irrelevant to the problem, but for ease of running this content locally:

#gridLoader {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(255, 255, 255, 0.8);
    z-index: 1000;
    display: none;
    align-items: center;
    justify-content: center;
}

Finally, here is my HomeController action:

        [HttpPost]
        public ActionResult ExcelExportSave(string contentType, string base64, string fileName)
        {
            var fileContents = Convert.FromBase64String(base64);
            return File(fileContents, contentType, fileName);
        }

 

 

 

 

My Environment and Project

This is an ASP.NET Core 8 MVC project being run in Visual Studio 2022 on a Windows laptop.  The project does use Microsoft Identity authentication and I am developing by running it on Localhost.  Notably, my HomeController currently has no authentication or authorization applied as I'm still just doing early stage development on localhost.  

Any assistance that could be provided or suggestions as to why this is failing would be appreciated.

 

 

Mihaela
Telerik team
 answered on 07 Jun 2024
1 answer
75 views

This was a wonderful solution to add checkboxes to a grid w/o needing to double click to enter the editor.

https://www.telerik.com/forums/how-can-i-have-a-checkbox-column-in-a-grid-without-having-to-click-on-the-checkbox-twice-to-edit#1557957

Is there a way to add a filter to the checkbox columns as well?

Tsvetomila
Telerik team
 answered on 06 Jun 2024
1 answer
62 views
I want to set child grid value as html attribute. I tried like this \\#=ImportName_InlowerCase\\#. this is not working in asp.net core CShtml files. Please help me to resolve this. 
Mihaela
Telerik team
 answered on 31 May 2024
Narrow your results
Selected tags
Tags
+? more
Top users last month
Will
Top achievements
Rank 2
Iron
Motti
Top achievements
Rank 1
Iron
Hester
Top achievements
Rank 1
Iron
Bob
Top achievements
Rank 3
Iron
Iron
Veteran
Thomas
Top achievements
Rank 2
Iron
Want to show your ninja superpower to fellow developers?
Top users last month
Will
Top achievements
Rank 2
Iron
Motti
Top achievements
Rank 1
Iron
Hester
Top achievements
Rank 1
Iron
Bob
Top achievements
Rank 3
Iron
Iron
Veteran
Thomas
Top achievements
Rank 2
Iron
Want to show your ninja superpower to fellow developers?
Want to show your ninja superpower to fellow developers?