Using JavaScript to call a Controller Function in an MVC project

Viewed 200

I'm working on an MVC ASP.NET core project with EF 6 which stores product data in a database and creates a processing and shipping list. An important feature of the project is for certain users to be able to change the order of this list manually, which I'm implementing using drag&drop cards. Each card is created by looping through an IEnumerable of the model in the View, with each

  • containing a partial which renders the card. The issue I'm having is that changing the order in the view doesn't inherently change the Product.Order attribute in the project. I need a way to access the model item of the card being moved and the model item.Order of the card being usurped inside the JavaScript drop function I have.

    I'm currently trying with var order = @Html.Raw(JsonConvert.SerializeObject(item.Order) as String);, but that doesn't seem to be exposing anything to JavaScript that I can work with. I have a second line exposing the entire object.

    Controller Method:

    public void ChangeOrder(Product p, int order)
            {
    
                //This creates a list of all of the jobs in the database sorted by descending Order
                var PList = from prod in _context.Products select prod;
                PList = PList.OrderByDescending(prod => prod.Order);
    
                int origin = p.Order;
                p.Order = order;
    
                if(origin > order)
                {
                    int it = order;
                    while (it < origin && it < PList.Count())
                    {
                        PList.ElementAt(it++).Order--;
                    }
                } else if(origin < order)
                {
                    int it = origin;
                    while (it < order && it < PList.Count())
                    {
                        PList.ElementAt(it++).Order--;
                    }
                } else
                {
                    return;
                }
            }
    

    Index View

    @using Newtonsoft.Json;
    @model IEnumerable<Project.Models.Product>
    
    
    @{
        ViewData["Title"] = "Index";
    }
    
    <h2>Index</h2>
    
    @*The list container*@
    <ul class="container" id="dragfield" style="width: 100%;">
        @*The first card is a minicontainer for the create partial view*@
        <li class="card popupsrc" onclick="callPopup">...</li>
        @foreach (var item in Model)
        {
            <li class="dragcard" draggable="true" style="float: left; padding: 8px">@item.Order @*For debugging purposes, this displays the order with the card*@
                @*THE FOLLOWING LINEs ARE NOT SECURED AND SHOULD BE CHANGED BEFORE PUBLISHING - FOR TESTING PURPOSES ONLY*@
                var order = @Html.Raw(JsonConvert.SerializeObject(item.Order) as String);
                var order = @Html.Raw(JsonConvert.SerializeObject(item) as String);
                <partial name="_ProductCard.cshtml" model="item" />
            </li>
        }
    </ul>
    

    site.js Drag & Drop Block

    //D&D Functions Start
    {
        var dragSrcElem = null;
    
        function dragStartHandler(event)
        {
            // Target (this) element is card being dragged.
            dragSrcElem = this;
    
            event.dataTransfer.effectAllowed = 'move';
            event.dataTransfer.setData('text/html', this.outerHTML);
    
            this.classList.add('dragElem');
        }
        function dragOverHandler(event)
        {
            if (event.preventDefault)
            {
                event.preventDefault(); //Enables dropping
            }
            this.classList.add('over');
    
            event.dataTransfer.dropEffect = 'move';
    
            return false;
        }
    
        function dragEnterHandler(event) {
            // this|event.target is the card being hovered over.
        }
    
        function dragLeaveHandler(event) {
            this.classList.remove('over');  // this|e.target is card previously hovered over.
        }
    
        function handleDrop(event) {
            // this/e.target is card about to be usurped.
    
            if (event.stopPropagation) {
                event.stopPropagation(); // Prevents browsers from redirecting and cancelling the drag&drop 
            }
    
            // If the card hasn't moved, do nothing
            if (dragSrcElem != this) {
                // Set the original card's HTML to the HTML of the card we dropped on
                this.parentNode.removeChild(dragSrcElem);
                var dropHTML = event.dataTransfer.getData('text/html');
                this.insertAdjacentHTML('beforebegin', dropHTML);
                //refocuses to the original card
                var dropElem = this.previousSibling;
    
    //LOCATION OF ISSUE
                //call the Controller function to change the Order Attribute of the relocated card's item
                PFS.Controllers.ChangeOrder(dropElem, this.order);
    //LOCATION OF ISSUE
    
                addDnDHandlers(dropElem);
    
            }
            this.classList.remove('over');
            return false;
        }
    
        function dragEndHandler(event) {
            // this|e.target is the original card.
            this.classList.remove('over');
        }
    
        function addDnDHandlers(elem) {
            elem.addEventListener('dragstart', dragStartHandler, false);
            elem.addEventListener('dragenter', dragEnterHandler, false)
            elem.addEventListener('dragover', dragOverHandler, false);
            elem.addEventListener('dragleave', dragLeaveHandler, false);
            elem.addEventListener('drop', dropHandler, false);
            elem.addEventListener('dragend', dragEndHandler, false);
    
        }
    
        var jc = document.querySelectorAll('#dragcard .dragcard');
        [].forEach.call(jc, addDnDHandlers);
    }
    //D&D Functions End
    

    I expect the JavaScript handleDrop function to call a function in the controller using parameters from the view which originally called the JS function. Currently the code I have implemented in the view does not expose the necessary information to the Javascript code in a way I know how to access, nor does JS recognize the Controller function

  • 1 Answers

    From your JavaScript code, I'm not sure what exactly PFS is in the context of PFS.Controllers.ChangeOrder... so I'm not sure I could help there.

    Personally, when I try to reach a controller action, I like to JavaScript's built-in fetch function. Essentially, you pass in the url as a parameter and an object if you want to specify any other details (like a body or something). It looks something like this:

    //Of course you should replace this with your actual url, this one is just for demo //purposes.
    const changeOrderUrl = "order/ChangeOrder"; 
    
    fetch(changeOrderUrl, {
        method: "POST", //or GET or whatever your controller action is
        body: JSON.stringify({
            p: dropElem,
            order: this.order
        }),
        headers: { "content-type": "application/json; charset=utf-8" }
    })
    .then(response => response.json())
    .then((responseData) => {
        //Do whatever you want with the response here. IF you don't want to do anything with 
        //the response, you can remove both of the .then()'s
    });
    

    Additionally, you'll most likely have to add a [FromBody] attribute to the controller action's parameters like this: public void ChangeOrder([FromBody]Product p, int order)

    Let me know if I can assist in any other way!

    Related