Search code examples
javascriptc#asp.net-mvcasp.net-corefetch-api

Pass data to controller with fetch in ASP NET


I want pass data from inputs of cards.I am passing data with fetch to the controller and there is no data in the controller.

script.js

var currentCardNumber = 1;

function addCard() {

    currentCardNumber++;
    let newCardHTML = `
                    <div class="header d-flex justify-content-between p-3">
                        <h4 class="card-counter">${currentCardNumber}</h4>
                    </div>
                    <div class="card-body row">
                        <div class="term col">
                            <h3>Term</h3>
                            <input class="form-control w-100" name = "term" >
                        </div>
                        <div class="definition col">
                            <h3>Definition </h3>
                            <input class="form-control w-100" name="definition" >
                        </div>
                    </div>
            </div> `;
    let newCard = document.createElement('div');
    newCard.classList.add('card');
    newCard.classList.add('mt-3');
    newCard.innerHTML = newCardHTML;

    let addNewCard = document.querySelector('.cards');
    let referenceNode = document.getElementById('create-cards');
    addNewCard.insertBefore(newCard, referenceNode);

}
function saveCardDataToServer() {
  let cards = [];
  document.querySelectorAll('.card').forEach((card) => {

    let term = card.querySelector('input[name="term"]').value.trim();
    let definition = card.querySelector('input[name="definition"]').value.trim();
    if (term && definition) {
      cards.push({
        Term: term,
        Definition: definition
      });
    }
  })
  fetch('/Cards/Create/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(cards)
    })
    .then(response => response.json())
    .then((data => {
      if (data.success) {
        console.log("Data successfully saved: ", data);
        window.location.href = "/Cards/Learning";
      } else {
        console.error("Error saving card: ", data.message);
      }
    }))
}

document.addEventListener('DOMContentLoaded', function() {
  let newCard = document.querySelector('.new-card');
  newCard.addEventListener('click', function() {
    addCard();
  });
  document.querySelector('form.cards').addEventListener('submit', function() {
    saveCardDataToServer();
  });
});

View

@model CardList

<!DOCTYPE html>
<html lang="en">

<head>
    <script src="~/js/script.js">
    </script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous" asp-append-version="true">    
</head>

<body>

<main class="main">
  <div class="container">
        <div class="new-module d-flex justify-content-between mt-5">
            <h1>Create new module</h1>
            <a class="btn btn-primary" asp-controller="Cards" asp-action="learning" type="submit">Создать</a>
        </div>

        <div class="card mt-5 p-2 pb-3">
            <h3>Title</h3>
            <input class="form-control w-100" placeholder="Enter a name">
        </div>
        <form asp-controller="Cards" asp-action="Create" method="post" class="cards">
            <div class="card mt-6" id="card">
                <div class="header d-flex justify-content-between p-3">
                    <h4 class="card-counter">1</h4>
                </div>
                <div class="card-body row">
                    <div class="term col">
                        <h3>Term</h3>
                        <input class="form-control w-100" name = "term" >
                    </div>
                    <div class="definition col">
                        <h3>Definition </h3>
                        <input class="form-control w-100" name = "definition"  >
                    </div>
                </div>
            </div>

            <input type="submit" value="Create" id="create-cards" class="btn btn-primary mt-2"/>
        </form>
        <button class="new-card w-100 d-flex justify-content-center align-items-center mt-4 mt-s rounded-2" style="height: 5rem"  type="button">
            <h3>+ Add card</h3>
        </button>
  </div>

</main>
</body>

</html>

Models

public class Card
{
    public int CardId { get; set; }
    [Required]
    public string Term { get; set; } = string.Empty;
    public string Definition { get; set; } = string.Empty;
}

public class CardList
{
    public List<Card> Cards { get; set; }
}

Controller

public class CardsController : Controller
{
    public IActionResult Learning()
    {
        var cards = CardsRepository.GetCards();
        return View(cards);
    }

    public IActionResult Create()
    {
        return View();
    }


    [HttpPost]
    [Route("Cards/Create/")]
    public IActionResult Create([FromForm] CardList list)
    {
        if (list != null)
        {
            foreach (var card in cards)
            {
                if (!string.IsNullOrWhiteSpace(card.Term) && !string.IsNullOrWhiteSpace(card.Definition))
                {
                    CardsRepository.AddCard(card);
                }
            }

            return Json(new { success = true, message = "Card saved successfully " });
        }

        var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
        return Json(new { success = false, message = "Validation error", errors = errors });
    }
}

I see data in DevTools from fetch, however I don't see data in controller through debugger.


Solution

  • I want pass data from inputs of cards.I am passing data with fetch to the controller and there is no data in the controller.

    Based on your shared code and description it seems, your issue seems to stem from the method of binding data in the controller.

    The FromForm attribute expects form-encoded data, but you are sending JSON data from your fetch request. To handle this correctly, you should either modify your client side code so that it can deal with [FromForm] data binding or change the attribute to FromBody to allow the controller to correctly parse the incoming JSON payload.

    I would suggest you to use FromBody in controller so that you need minimum change or modification because your client code is currently designed for FromBody jason data interation.

    In order to do that, modify following code:

    function saveCardDataToServer() {
        let cards = [];
        document.querySelectorAll('.card').forEach((card) => {
            let termInput = card.querySelector('input[name="term"]');
            let definitionInput = card.querySelector('input[name="definition"]');
    
            if (termInput && definitionInput) {
                let term = termInput.value.trim();
                let definition = definitionInput.value.trim();
                if (term && definition) {
                    cards.push({
                        Term: term,
                        Definition: definition
                    });
                }
            } else {
                console.error("Input elements not found in card:", card);
            }
        });
    

    Note: Above method within your snippet would cause glitch because, If any .card element does not contain an input element with the name term or definition, querySelector returns null. The line let term = card.querySelector('input[name="term"]').value.trim(); will cause a TypeError because null does not have a value property. So in order to fix that, I have modified that, so it includes a check (if (termInput && definitionInput)) to ensure that the input elements were found before trying to access their value properties.

    Modify Request Body:

     body: JSON.stringify({ Cards: cards })
    

    Controller:

     public IActionResult Create([FromBody] CardList Cards)
    

    Output:

    enter image description here

    enter image description here