Seeking guidance on creating a unique ASP.NET Custom Control with two grid elements. Any tips or

Looking for guidance on how to create a custom control that utilizes two grids with an Add and Remove button in between.

When the Add button is clicked, the selected item from the left grid should be moved to the right grid and removed from the left grid. The Remove button should do the opposite.

I am aware that Javascript will likely need to be used to achieve a smooth user experience.

Currently, I am working on a control that inherits CompositeControl and includes two grids and two sources. I am considering using an UpdatePanel to avoid a complete post back when performing the Add/Remove actions.

Would appreciate any suggestions on the best approach to tackle this design challenge.

Answer №1

I have utilized Kendo to create this sample. I have included and excluded certain sections for supervisors in my project. Below is the main Action you need:

        public ActionResult AddPathToSupervisor()
    {
                return View();
    }

In my sample, the view allows you to select a supervisor and then add paths to them. You will need 2 grids and 2 buttons in the view as shown below:

<div class="row">
<div class="col large">
    @(Html.Kendo().ComboBox()
    .Name("supervisor")
    .BindTo(SupervisorsSelectList)//var DocumetTypesSelectList = ViewBag.DocumetTypesSelectList as List<SelectListItem> ?? new List<SelectListItem>();
    .Events(e => e.Change("changeSupervisor"))

    )
</div>
</div>

<div class="row">
    <div class="col medium">
        <p>New Paths</p>
    </div>
    <div class="col medium">
        <p></p>
    </div>
    <div class="col medium">
        <p>Supervisor Paths</p>
    </div>

</div>

<div class="row">
    <div class="col medium">
        @(Html.Kendo().Grid<Distribution.Models.Path>()
            .Name("newPathsGrid")
            .Columns(columns =>
            {
                columns.Bound(p => p.PathId).Visible(false);
                columns.Bound(p => p.Title).Title(PathResource.Paths);
            })
            .Sortable()
            .Scrollable()
            .Navigatable()
            .Filterable(filterable => filterable.Extra(false))
                //.HtmlAttributes(new { style = "height:480px;" })
            .Resizable(resize => resize.Columns(true))
            .Selectable(s => s.Mode(GridSelectionMode.Multiple))
            .DataSource(dataSource => dataSource
                .Ajax()
                //.PageSize(15)
                .Events(events => events.Error("error_handler"))
                .Model(model =>
                {
                    model.Id(p => p.PathId);
                    model.Field(p => p.PathId).DefaultValue(new Guid());
                })
                .Read(read => read.Action("FillNewSupervisorPathsGrid", "Paths"))
            )
        )
    </div>


<div class="col medium">
        <input type="button" id="addPathToSupervisor" value=">>Add>>" />
        <input type="button" id="removePathFromSupervisor" value="<<Remove<<" />
    </div>


<div class="col medium k-rtl">
        @(Html.Kendo().Grid<Distribution.Models.Path>()
            .Name("supervisorPathGrid")
            .Columns(columns =>
            {
                columns.Bound(p => p.PathId).Visible(false);
                columns.Bound(p => p.Title).Title(PathResource.Paths);
            })

        //.Pageable()
            .Sortable()
            .Scrollable()
            .Navigatable()
            .Filterable(filterable => filterable.Extra(false))
                //.HtmlAttributes(new { style = "height:480px;" })
            .Resizable(resize => resize.Columns(true))
            .Selectable(s => s.Mode(GridSelectionMode.Multiple))
            .DataSource(dataSource => dataSource
                .Ajax()
                //.PageSize(15)
                .Events(events => events.Error("error_handler"))
                .Model(model =>
                {
                    model.Id(p => p.PathId);
                    model.Field(p => p.PathId).DefaultValue(new Guid());
                })
                .Read(read => read.Action("FillSupervisorPathsGrid", "Paths", new { id = ViewBag.SupervisorId }))
            )
        )
    </div>
</div>

This JavaScript code is used to select the Supervisor's ID:

<script type="text/javascript">
function changeSupervisor(e) {
    var id = this.value();
    var supervisorPathGrid = $("#supervisorPathGrid").data("kendoGrid");
    supervisorPathGrid.dataSource.read({ id: id });
}

Below is the JavaScript code to add and remove paths:

<script type="text/javascript">
var supervisorPathGrid = $("#supervisorPathGrid").data("kendoGrid");
var newPathsGrid = $("#newPathsGrid").data("kendoGrid");
var selectedItem = $("#supervisor").data("kendoComboBox");

$(document).on('click', '#addPathToSupervisor', function (e) {
    e.preventDefault();
    var supervisorId = selectedItem.value();
    if (hasManyRowSelected(newPathsGrid)) {
        var values = [];
        values.push({
            name: "supervisorId",
            value: supervisorId
        });
        newPathsGrid.select().each(function () {
            values.push({
                name: "ids",
                value: newPathsGrid.dataItem(this).PathId
            });
        });
        $.ajax({
            url: '@Url.Action("AddPathToSupervisor")',
            type: 'POST',
            datatype: "json",
            traditional: true,
            data: values,
            success: function () {
                newPathsGrid.select().each(function () {
                    var $this = $(this);
                    var data = newPathsGrid.dataItem($this);
                    supervisorPathGrid.dataSource.insert(0, data);
                });
                newPathsGrid.select().each(function () {
                    var $this = $(this);
                    var data = newPathsGrid.dataItem($this);
                    newPathsGrid.removeRow($this);
                });
            },
            beforeSend: function () {
                $('#addPathToSupervisor').attr("disabled", true);
                $('#addPathToSupervisor').addClass("ajax-load");
            },
            error: function (event, request, settings) {
                ajax_exception(event);
            },
            complete: function () {
                $('#addPathToSupervisor').attr("disabled", false);
                $('#addPathToSupervisor').removeClass("ajax-load");
                grid.dataSource.read();
            },
            timeout: 50000
        });
    }
});
$(document).on('click', '#removePathFromSupervisor', function (e) {
    e.preventDefault();
    var supervisorId = selectedItem.value();
    if (hasManyRowSelected(supervisorPathGrid)) {
        var values = [];
        supervisorPathGrid.select().each(function () {
            values.push({
                name: "ids",
                value: supervisorPathGrid.dataItem(this).PathId
            });
        });
        $.ajax({
            url: '@Url.Action("RemovePathFromSupervisor")',
            type: 'POST',
            datatype: "json",
            traditional: true,
            data: values,
            success: function () {
                supervisorPathGrid.select().each(function () {
                    var $this = $(this);
                    var data = supervisorPathGrid.dataItem($this);
                    newPathsGrid.dataSource.insert(0, data);
                });
                supervisorPathGrid.select().each(function () {
                    var $this = $(this);
                    var data = supervisorPathGrid.dataItem($this);
                    supervisorPathGrid.removeRow($this);
                });
            },
            beforeSend: function () {
                $('#removePathFromSupervisor').attr("disabled", true);
                $('#removePathFromSupervisor').addClass("ajax-load");
            },
            error: function (event, request, settings) {
                ajax_exception(event);
            },
            complete: function () {
                $('#removePathFromSupervisor').attr("disabled", false);
                $('#removePathFromSupervisor').removeClass("ajax-load");
                grid.dataSource.read();
            },
            timeout: 50000
        });
    }
});

Next, you will need two POST methods to add and remove paths:

    [HttpPost]
    public ActionResult AddPathToSupervisor(string[] ids, string supervisorId)
    {
        try
        {
            PathsBll.AddPathsToSupervisor(ids, supervisorId);
        }
        catch (Exception ex)
        {
            throw ex;
        }
        return Json(ModelState.ToDataSourceResult());
    }

    [HttpPost]
    public ActionResult RemovePathFromSupervisor(string[] ids)
    {
        try
        {
            PathsBll.RemovePathsFromSupervisor(ids);
        }

        catch (Exception ex)
        {
            throw ex;
        }
        return Json(ModelState.ToDataSourceResult());
    }

You can use LINQ to add or remove paths based on their IDs. Familiarity with Kendo will help understand how to populate each grid. Feel free to ask for more details if needed. Good luck!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing a strategy to prevent the browser's back button from functioning

Is there a way to prevent the user from using the back button in a single page application? I've tried methods like onhashchange and window.history.forward, but they don't seem to be effective (perhaps because the URL doesn't change). ...

How come my data is not appearing on the screen despite receiving a result in the console log?

I am facing an issue with displaying data obtained from Supabase. I am passing the data as a prop and using map to iterate over it in order to show the required values. However, despite logging the correct results for `programme.title`, nothing is being re ...

Received a TypeError while trying to implement routing with Express and ES6

Using Express and ES6, I am attempting to route in the following manner: index.js import express from 'express' import bodyParser from 'body-parser' import appRoutes from './routes/appRoutes.js' var app = express(); app.use( ...

Unable to execute JavaScript code from the back-end

While I am aware that my issue is quite common and similar queries have been asked before, my problem presents a unique challenge. I am currently developing a web application using ASP.Net 4.0. On one of the pages, I have implemented an AJAX Toolkit 4 Cale ...

Acquire HTML table information in array format using JavaScript

I am searching for a script that can extract the 2D array of rows and columns from an HTML table within a div element. The desired array output should be: [ ["Company", "Contact", "Country"], ["Alfreds Futterkiste", "Maria Anders", "Germany"], ...

Clickable list element with a button on top

Within my web application, there is a list displaying options for the user. Each 'li' element within this list is clickable, allowing the user to navigate to their selected option. Additionally, every 'li' element contains two buttons - ...

Bizarre text scenarios in JavaScript

I've come across something similar in certain libraries if ("testing" !== 'production') { "testing" !== 'production' ? warning(this instanceof Constructor, 'A React component is being called directly. Consider using ...

What steps should I take to resolve the "You do not have authorization to access this directory or page" error in Azure App Services?

Currently, I am attempting to deploy my Next.js 13 application to AppServices using Github Actions. The compilation process on Github was successful, however, I am encountering an issue where my application does not seem to be deploying or starting up prop ...

What are some effective methods for optimizing GLSL/C code in Three.JS when using PerObject-Blur?

UPDATE 2 I have successfully integrated custom attributes using THREE.js. Each pass in the vertex shader is now aligned with the position attribute, resulting in an efficient solution with minimal code. An example will be added later UPDATE 1 This me ...

Ways to disseminate arguments when dealing with an array of arrays in JavaScript

Struggling to pass an array as arguments into the join method on path in node, but hitting a roadblock: var path = require("path"); var paths = [__dirname]; var userInput = ["app", "js"]; paths.push(userInput); var target = path.join.apply(null, paths); ...

Issues encountered when trying to use the export default syntax in Vue components

Programming Issue I attempted to execute the provided code, but unfortunately, it does not work and no output is visible in the browser. Is there an alternative method for writing code within <scripts setup> </script>? I have understood that f ...

Sending a document to a nodeJS server

Client code: var data = new FormData(); data.append(fileName, blob, 'test.html'); fetch('http://localhost:3000/', { method: 'POST', headers: { }, body: data }).then( response => { console.log(res ...

Do two Express JS applications run on separate threads?

Running two express apps on different ports raises the question of whether they assign themselves to different cores, knowing that express typically runs on a single thread. The cluster module, however, replicates the express app and allows them to utiliz ...

Displaying a PHP variable on the console through the power of jQuery and AJAX

I'm attempting to display the value of a PHP variable in the browser's console using jQuery and AJAX. I believe that the $.ajax function is the key to achieving this. However, I am unsure about what to assign to the data parameter and what should ...

Is it possible to execute a task following a particular Websocket.send() in JavaScript?

After sending a message to a websocket, I am trying to send a POST request using callbacks. I attempted the following approaches: socket.send(message,function(){...}); and function sendsocket(message, callback){ socket.send(message); callback; } and ca ...

Finding the Angular alternative for $http $resource when making a PDF GET request

In my Angular project, I utilize $resource for all the requests to the Web API. However, I recently encountered an issue with handling data from a request for a PDF file. After some research, I came across a snippet that worked perfectly using $http. Despi ...

position property in div element disrupts slider functionality

I've been working on incorporating a simple slider into my website and stumbled upon this example on jsfiddle My goal is to have the slider positioned "relative" within my site, but when I change the CSS to position: relative;, the slider no longer ...

Is it possible to automatically play a sound clip when the page loads?

Is there a way to automatically play a sound clip when my page loads? Are there any specific JavaScript or jQuery methods I can use for this, as I am developing my page using PHP. ...

How can I parse JSON in React using the Parse function?

I am currently working with three list components, and when I click on any item in the tree list, it displays the JSON data. However, I would prefer to view it in a parse format rather than JSON. I attempted to use let json = JSON.parse(this.props.node, n ...

Tips for getting the setInterval function to work with a progress bar even when a tab is not in focus

After browsing through similar questions, I couldn't find the answer I was looking for. As a newbie in programming experimenting with JavaScript Progress Bar, I encountered an issue where the progress bar would pause and the counter wouldn't coun ...