I'm a junior developer (couple years into it) and very new to AJAX.
I'm writing an internal inventory application built on MVC and right now I'm dealing with adding and removing items to/from a cart. When the user select an item to take in or out, s/he is brought to the cart page, where they can choose to add or diminish the number of item to charge or dismiss. This is the appearance of part of the page (just for reference: "Marca" stands for Manufacturer, "Modello" for Model)
This is the cshtml code for the whole partial view:
@using Magazzino.Domain.Entities
@model Magazzino.WebUI.Models.CarrelloViewModel
@{
ViewBag.Title = "Carrello";
}
<h2>Carrello</h2>
<table class="table carrello">
<thead>
<tr>
<th>Categoria</th>
<th>Marca</th>
<th>Modello</th>
<th>Colore</th>
<th>Note</th>
<th>Quantità</th>
</tr>
</thead>
<tbody>
@foreach (Articolo articolo in Model.Carrello.Articoli)
{
<tr>
<td class="text-left">@articolo.Item.Categoria</td>
<td class="text-left">@articolo.Item.Marca</td>
<td class="text-left">@articolo.Item.Modello</td>
<td class="text-left">@articolo.Item.Colore</td>
<td class="text-left">@articolo.Item.Note</td>
<td class="carrello-qta">
<div class="qtaAggiungi btn btn-success" data-action-url="@Url.Action("AumentaODiminuisci", "Carrello")" data-item-marca="@articolo.Item.Marca" data-item-modello="@articolo.Item.Modello">+</div>
<input class="form-control" type="text" data-item-marca="@articolo.Item.Marca" data-item-modello="@articolo.Item.Modello" value="@articolo.Qta" name="Qta" />
<div class="qtaDiminuisci btn btn-success" data-action-url="@Url.Action("AumentaODiminuisci", "Carrello")" data-item-marca="@articolo.Item.Marca" data-item-modello="@articolo.Item.Modello">-</div>
</td>
</tr>
}
</tbody>
</table>
<div class="text-center">
<a class="btn btn-primary" href="@Model.ReturnUrl">Prosegui la consultazione</a>
</div>
while this is the page ViewModel:
public class CarrelloViewModel
{
public Carrello Carrello { get; set; }
public string ReturnUrl { get; set; }
}
and this is the cart class
public class Carrello
{
private List<Articolo> ElencoArticoli = new List<Articolo>();
public void AggiungiArticolo(Item item, int qta)
{
Articolo articolo = ElencoArticoli.Where(i => i.Item == item).FirstOrDefault();
if (articolo == null)
{
ElencoArticoli.Add(new Articolo { Item = item, Qta = qta });
}
}
public void RimuoviArticolo(Item item)
{
ElencoArticoli.RemoveAll(i => i.Item == item);
}
public void CancellaTutto()
{
ElencoArticoli.Clear();
}
public void ModificaArticolo(Item item, int qta)
{
Articolo articolo = ElencoArticoli.Where(i => i.Item == item).FirstOrDefault();
if (articolo == null) throw new ArgumentOutOfRangeException("L'articolo non è presente nel carrello");
articolo.Qta = qta;
}
public void AumentaODiminuisciArticolo(Item item, int qta)
{
Articolo articolo = ElencoArticoli.Where(i => i.Item == item).FirstOrDefault();
if (articolo == null) throw new ArgumentOutOfRangeException("L'articolo non è presente nel carrello");
articolo.Qta += qta;
}
public List<Articolo> Articoli
{
get { return ElencoArticoli; }
}
}
public class Articolo
{
public Item Item { get; set; }
public int Qta { get; set; }
}
The buttons in the page are each bound to the relevant AJAX post, both within the classic $(document).ready(function() {})
$('.qtaAggiungi').on('click', function () {
self = $(this);
// get the corrispondent quantity textbox element
var qtaCorrente = $("input[name='Qta']").filter(function () {
return ($(this).data()["itemMarca"] === self.data()["itemMarca"])
&& ($(this).data()["itemModello"] === self.data()["itemModello"]);
});
var ajaxRequest = $.ajax({
url: $(this).data()["actionUrl"],
type: "post",
data: {
marca: $(this).data()["itemMarca"],
modello: $(this).data()["itemModello"],
qta: 1,
}
}).done(function (data) {
qtaCorrente.val(data);
});
})
$('.qtaDiminuisci').on('click', function () {
self = $(this);
var qtaCorrente = $("input[name='Qta']").filter(function () {
return ($(this).data()["itemMarca"] === self.data()["itemMarca"]) && ($(this).data()["itemModello"] === self.data()["itemModello"]);
});
if (qtaCorrente.val() > 1) {
var ajaxRequest = $.ajax({
url: $(this).data()["actionUrl"],
type: "post",
data: {
marca: $(this).data()["itemMarca"],
modello: $(this).data()["itemModello"],
qta: -1,
}
}).done(function (data) {
qtaCorrente.val(data);
});
}
})
and this is the action method in the controller (comments added her for clarity because of translation):
public int AumentaODiminuisci(string marca, string modello, int qta)
{
// getting the inventory List from repository
List<ElementoInventario> inventario = Repository.GetInventario();
// getting the correspondent item in the inventory
Item itemDaModificare = inventario
.Where(i => i.Item.Marca == marca && i.Item.Modello == modello)
.FirstOrDefault().Item;
if (itemDaModificare != null)
{
// modify the quantity of that item in the cart
GetCarrello().AumentaODiminuisciArticolo(itemDaModificare, qta);
}
// return the update quantity
return GetCarrello().Articoli
.Where(i => i.Item.Marca == marca && i.Item.Modello == modello)
.FirstOrDefault().Qta;
}
private Carrello GetCarrello()
{
Carrello carrello = (Carrello)Session["Carrello"];
if (carrello == null)
{
carrello = new Carrello();
Session["Carrello"] = carrello;
}
return carrello;
}
My main doubt is the fact that I initially loaded the value of the quantity textbox through Razor, but after every modification, the displayed value and the model are -let's say- "disjointed". Sure, the Model gets updated and so is the displayed value and they are (or should be!) always equal, but I'm questioning myself whether it is fine to call a method that returns a simple integer value and then update the textbox with that value, or is a bad practice that might lead to bugs (I also see now that I have a method that might throw an exception but I'm not catching it).
Also, I feel like the way I find the textbox in the beginning of the javascript function is way too "hacky" and not very performing.
.FirstOrDefault().Item;
doesn't make sense. TheOrDefault()
implies the result can be null, but your.Item
always assumes it isn't. \$\endgroup\$