Dodawanie kodu JavaScript w aplikacji ASP.NET Core
Jak już wspomniałem, dzisiaj będziemy pisali wiele kodu w JavaScript'cie, ponieważ chcemy, żeby wykonywał się on w samej przeglądarce po stronie klienta. Żeby dodać kod JavaScript w widoku Index, to musimy sobie dodać odpowiednią sekcję, to znaczy sekcję Scripts. Działa to tak, ponieważ w naszym szablonie, czyli w pliku _Layout mamy na samym dole właśnie renderowanie tej sekcji skryptowej.
@await RenderSectionAsync("Scripts", required: false)
Zatem możemy sobie w naszych widokach dodać sekcję Scripts i zostanie ona wyświetlona dokładnie w tym miejscu na stronie internetowej, czyli pod wszystkimi skryptami i przed zamknięciem tagu body.
@section Scripts
{
}
Także tutaj chcemy zamieścić kod w JavaScript'cie. Możemy tutaj jeszcze dodać tag script text/javascript.
@section Scripts
{
<script type="text/javascript">
</script>
}
Tutaj będzie cała nasza logika po stronie klienta. Może na początek spróbujmy sobie wyświetlić za pomocą jQuery po załadowaniu się dokumentu odpowiednią wartość dla naszego TextBox. Może to wyglądać w ten sposób:
@section Scripts
{
<script type="text/javascript">
$(document).ready(function () {
$("#ScreenInput").val('0');
})
</script>
}
Czyli do metody ready przekazujemy funkcje i odwołujemy się do naszego screenInput, czyli do naszego TextBox'a po jego Id, to znaczy #ScreenInput i ustawiamy za pomocą jQuery wartość 0. Jeżeli teraz uruchomię przeglądarkę (Ctrl+F5), to zauważ, że na naszym TextBoxie jest już to 0 widoczne.
Do tej kontrolki ScreenInput będziemy się odwoływać kilka razy, także możemy sobie wprowadzić wyżej nową zmienną, gdzie będziemy tę kontrolkę przechowywać. Dzięki temu będziemy się później tylko odnosić poprzez nazwę zmiennej, także będzie to troszkę wygodniejsze.
@section Scripts
{
<script type="text/javascript">
var screenInput = $("#ScreenInput");
$(document).ready(function () {
screenInput.val('0');
})
</script>
}
Implementacja podstawowych funkcji w JavaScript
Dodamy teraz jeszcze funkcje, które zaraz powiążemy z naszymi przyciskami. Nasza metoda może się nazywać addNumber.
function addNumber(number) {
}
Jako parametr będzie przekazana liczba, która zostanie kliknięta. Tak samo będzie funkcja addOperation, clearScreen oraz getResult.
function addNumber(number) {
}
function addOperation(operation) {
}
function clearScreen() {
}
function getResult() {
}
Powiązanie przycisków z funkcjami JavaScript
Następnie te funkcje musimy powiązać z naszymi przyciskami. Nie jest to nic skomplikowanego, wystarczy tutaj przypisać do zdarzenia onclick odpowiednią nazwę metoda, która ma zostać wywołana i ewentualnie można przekazać również parametr.
<button type="button" class="btn bg-success" onclick="addNumber('7')">7</button>
Podobnie teraz robimy we wszystkich miejscach, to znaczy na razie dla wszystkich cyfr.
<button type="button" class="btn bg-success" onclick="addNumber('7')">7</button>
<button type="button" class="btn bg-success" onclick="addNumber('8')">8</button>
<button type="button" class="btn bg-success" onclick="addNumber('9')">9</button>
<button type="button" class="btn bg-success" onclick="addNumber('4')">4</button>
<button type="button" class="btn bg-success" onclick="addNumber('5')">5</button>
<button type="button" class="btn bg-success" onclick="addNumber('6')">6</button>
<button type="button" class="btn bg-success" onclick="addNumber('1')">1</button>
<button type="button" class="btn bg-success" onclick="addNumber('2')">2</button>
<button type="button" class="btn bg-success" onclick="addNumber('3')">3</button>
<button type="button" class="btn bg-success zero-button" onclick="addNumber('0')">0</button>
<button type="button" class="btn bg-success" onclick="addNumber(',')">,</button>
Implementacja metod w JavaScript
Następnie dla operacji mamy osobną metodę, to znaczy addOperation, także również dla operacji ustawimy odpowiednią metodę.
<button type="button" class="btn bg-secondary" onclick="addOperation('/')">/</button>
<button type="button" class="btn bg-secondary add-button" onclick="addOperation('+')">+</button>
<button type="button" class="btn bg-secondary" onclick="addOperation('-')">-</button>
<button type="button" class="btn bg-secondary" onclick="addOperation('*')">*</button>
Dla wyświetlenia wyniku mamy metodę getResult i do czyszczenia ekranu mamy metodę o nazwie clearScreen. Także również przypiszmy te metody do zdarzenia onclick.
<button type="button" class="btn bg-primary result-button" onclick="getResult()">=</button>
<button type="button" class="btn bg-danger" onclick="clearScreen()">C</button>
Super, czyli mamy już wszystkie metody przypisane do przycisków. Pozostaje nam jeszcze tylko zaimplementowanie logiki w naszych funkcjach. Także to zrobimy bardzo podobnie do tego, co robiliśmy już wcześniej w aplikacji WPF. Tylko tym razem napiszemy ten kod w JavaScript. Przejdźmy na początek do metody addNumber.
function addNumber(number) {
var val = screenInput.val();
if (val == '0' && number != ',')
val = '';
else if (number == ',' && availableOperations.includes(val.slice(-1)))
number = '0,';
screenInput.val(val + number);
}
Pobieramy sobie wartość naszego TextBox'a. Następnie sprawdzamy, czy wartość jest równa zero i czy wartość tego parametru jest różna od przecinka. Czyli chcemy sprawdzić, czy jest to po prostu jakaś liczba. Jeżeli tak, to w tym przypadku przypisujemy do zmiennej val pustego string'a. W przeciwnym przypadku, jeżeli number to jest przecinek, czyli ten nasz parametr jest przecinkiem, to weryfikujemy jeszcze ostatni znak w TextBox'ie i sprawdzamy, czy jest to operacja.
var availableOperations = ['-', '+', '/', '*'];
Czyli jeżeli ten parametr był przecinkiem i ostatnim znakiem w TextBox'ie była operacja, to chcemy, żeby tam jeszcze zostało doklejone 0. Następnie w metodzie addNumber uzupełniamy wartość naszego TextBox'a o przekazany parametr, to znaczy doklejamy zmienną number do wartości, która już wcześniej była wyświetlana na ekranie. Następna metoda, którą musimy zaimplementować to addOperation.
function addOperation(operation) {
screenInput.val(screenInput.val() + operation);
}
Tutaj doklejamy do tego, co już mamy w TextBoxie przekazany parametr, czyli operację. Następnie metoda clearScreen.
function clearScreen() {
screenInput.val('0');
}
Tutaj tylko ustawiamy screenInput na 0, czyli na tę wartość początkową.
Używanie ajax'a w aplikacji ASP.NET Core
Na koniec będziemy chcieli, aby funkcja getResult wyświetlała na ekranie wynik. Będzie to trochę bardziej skomplikowane, ale również sobie z tym poradzimy. Będziemy chcieli w tym miejscu wywołać akcję z naszego kontrolera, czyli będziemy to chcieli zrobić po stronie C#. Także moglibyśmy w tym miejscu wywołać po prostu akcje z kontrolera, tylko wtedy, jeżeli byśmy ją wywalił wprost, to musiałby zostać odświeżony cały widok kalkulatora, to znaczy musielibyśmy załadować tę stronę ponownie i wyświetlić na niej wynik. Jednak nie jest to dobre rozwiązanie i my chcemy to zrobić troszkę inaczej. Nie chcemy, żeby po kliknięciu w wynik było odświeżanie strony, ale chcemy, żeby po prostu wartość tego TextBox'a (samego TextBox'a) zmieniła się automatycznie bez odświeżania strony. Dlatego w tej metodzie musimy skorzystać z ajax'a, który nam pozwala właśnie na takie rzeczy. Wywołanie ajaxa, może wyglądać w ten sposób:
function getResult() {
$.ajax({
type: 'POST',
url: "@Url.Action("Result", "Home")",
data: {
inputVal: screenInput.val()
},
success: function (data) {
if (data.success)
screenInput.val(data.result);
else
alert(data.message);
},
error: function (data) {
alert(data.message);
},
dataType: "json"
});
}
Wywołujemy metodę ajax i przekazujemy odpowiednie parametry. Wskazujemy, że ma zostać wywołana metoda post w naszym kontrolerze. Tę metodę za chwilę napiszemy po stronie C#. Chcemy, żeby została właśnie wywołana odpowiednia akcja. Będzie to akcja Result w kontrolerze Home. Zaraz przejdziemy do implementacji tej akcji. Do tej akcji chcemy przekazać parametr inputVal. Będzie to właśnie wartość, która jest w TextBox'ie. Jeżeli wszystko się uda, to będziemy chcieli obsłużyć to w ten sposób, że jeżeli będzie sukces, to wyświetlimy wartość zwróconą z tej akcji w TextBox'. Jeżeli jednak coś się nie powiedzie w naszym kontrolerze, to wyświetlimy wiadomość na ekranie. Jeżeli wystąpi jakiś błąd, to również go wyświetlimy na ekranie. Na koniec ustawiamy jeszcze typ na json. Czyli tak może wyglądać nasz ajax. Potrzebujemy teraz jeszcze zaimplementować metodę Result w kontrolerze Home.
Implementacja akcji w kontrolerze w ASP.NET Core
Jak przejdziesz do kontrolera o nazwie HomeController, to możesz zauważyć, że są tam już zaimplementowane inne akcje, które zostały stworzone razem z szablonem MVC. Dzięki nim wyświetlane mogą być podstawowe widoki szablonu. My tutaj dodamy nową akcję Result, która będzie przyjmowała parametr inputVal typu string i będzie zwracała json'a. Nasza metoda musi zostać również oznaczona jako HttpPost.
private DataTable _dataTable = new DataTable();
[HttpPost]
public IActionResult Result(string inputVal)
{
try
{
var result = Math.Round(Convert.ToDouble(_dataTable.Compute(inputVal.Replace(",", "."), "")), 2).ToString();
return Json(new { success = true, result });
}
catch (Exception exception)
{
//logowanie
return Json(new { success = false, message = exception.Message });
}
}
Wewnątrz metody wykonujemy odpowiednie obliczenia. Tutaj już ten kod jest bardzo podobny, do kodu, który wcześniej zaimplementowaliśmy w WPF'ie. Obliczeń dokonujemy za pomocą metody Compute obiektu klasy DateTable. Następnie rzutujemy wynik na double'a i zaokrąglamy do 2 miejsc po przecinku. Na koniec zwracamy nowy obiekt json'a. Jeżeli w naszej metodzie nie wystąpi żaden nieoczekiwany wyjątek, to zwrócimy success true oraz wartość obliczeń. Jeżeli wystąpi wyjątek, to wtedy success ustawiamy na falce i zwracamy jako message wiadomość błędu.
Uruchomienie i testy projektu
Możemy przebudować i uruchomić aplikację.
Wygląda na to, że podstawowe operacje działają bardzo dobrze, ale mamy w tym miejscu jeszcze 1 problem. Musimy dodać tak samo zabezpieczenie, jak to robiliśmy w WPF'ie, czyli żeby nie było można kliknąć wiele operacji z rzędu.
Blokowanie przycisków
Także dodamy sobie w Index.cshtml nową metodę, która będzie nam blokować i odblokowywać przyciski operacji na podstawie przekazanego parametru.
function setOperationBtnsAvailable(state) {
$('.operation').prop('disabled', !state);
}
Wywołamy ją w metodach:
function addNumber(number) {
var val = screenInput.val();
if (val == '0' && number != ',')
val = '';
else if (number == ',' && availableOperations.includes(val.slice(-1)))
number = '0,';
screenInput.val(val + number);
setOperationBtnsAvailable(true);
}
function addOperation(operation) {
screenInput.val(screenInput.val() + operation);
setOperationBtnsAvailable(false);
}
function clearScreen() {
screenInput.val('0');
setOperationBtnsAvailable(true);
}
I musimy jeszcze dodać nową klasę Operation do wszystkich przycisków operacji, które chcemy zablokować.
<button type="button" class="btn bg-secondary operation" onclick="addOperation('/')">/</button>
<button type="button" class="btn bg-secondary add-button operation" onclick="addOperation('+')">+</button>
<button type="button" class="btn bg-secondary operation" onclick="addOperation('-')">-</button>
<button type="button" class="btn bg-secondary operation" onclick="addOperation('*')">*</button>
<button type="button" class="btn bg-primary result-button operation" onclick="getResult()">=</button>
Przebudujmy teraz naszą aplikację i przejdź proszę do przeglądarki. Możemy teraz przetestować naszą aplikację i zobaczymy czy wszystko działa.
Wygląda, że wszystko działa w porządku. Nasza aplikacja jest już gotowa, wydaje się, że wszystko działa według założeń.
Kod całej aplikacji
Index.cshtml:
@{
ViewData["Title"] = "Kalkulator";
}
<div class="card mt-5">
@Html.TextBox("ScreenInput", null, new { @class = "calculator-screen", disabled = "disabled" })
<div class="calculator-btns">
<button type="button" class="btn bg-success" onclick="addNumber('7')">7</button>
<button type="button" class="btn bg-success" onclick="addNumber('8')">8</button>
<button type="button" class="btn bg-success" onclick="addNumber('9')">9</button>
<button type="button" class="btn bg-secondary operation" onclick="addOperation('/')">/</button>
<button type="button" class="btn bg-secondary add-button operation" onclick="addOperation('+')">+</button>
<button type="button" class="btn bg-success" onclick="addNumber('4')">4</button>
<button type="button" class="btn bg-success" onclick="addNumber('5')">5</button>
<button type="button" class="btn bg-success" onclick="addNumber('6')">6</button>
<button type="button" class="btn bg-secondary operation" onclick="addOperation('-')">-</button>
<button type="button" class="btn bg-success" onclick="addNumber('1')">1</button>
<button type="button" class="btn bg-success" onclick="addNumber('2')">2</button>
<button type="button" class="btn bg-success" onclick="addNumber('3')">3</button>
<button type="button" class="btn bg-secondary operation" onclick="addOperation('*')">*</button>
<button type="button" class="btn bg-primary result-button operation" onclick="getResult()">=</button>
<button type="button" class="btn bg-success zero-button" onclick="addNumber('0')">0</button>
<button type="button" class="btn bg-success" onclick="addNumber(',')">,</button>
<button type="button" class="btn bg-danger" onclick="clearScreen()">C</button>
</div>
</div>
@section Scripts
{
<script type="text/javascript">
var screenInput = $("#ScreenInput");
var availableOperations = ['-', '+', '/', '*'];
$(document).ready(function () {
screenInput.val('0');
})
function addNumber(number) {
var val = screenInput.val();
if (val == '0' && number != ',')
val = '';
else if (number == ',' && availableOperations.includes(val.slice(-1)))
number = '0,';
screenInput.val(val + number);
setOperationBtnsAvailable(true);
}
function addOperation(operation) {
screenInput.val(screenInput.val() + operation);
setOperationBtnsAvailable(false);
}
function clearScreen() {
screenInput.val('0');
setOperationBtnsAvailable(true);
}
function getResult() {
$.ajax({
type: 'POST',
url: "@Url.Action("Result", "Home")",
data: {
inputVal: screenInput.val()
},
success: function (data) {
if (data.success)
screenInput.val(data.result);
else
alert(data.message);
},
error: function (data) {
alert(data.message);
},
dataType: "json"
});
}
function setOperationBtnsAvailable(state) {
$('.operation').prop('disabled', !state);
}
</script>
}
HomeController:
using Microsoft.AspNetCore.Mvc;
using System;
using System.Data;
namespace Calculator.AspNetCoreApp.Controllers
{
public class HomeController : Controller
{
private DataTable _dataTable = new DataTable();
[HttpPost]
public IActionResult Result(string inputVal)
{
try
{
var result = Math.Round(Convert.ToDouble(_dataTable.Compute(inputVal.Replace(",", "."), "")), 2).ToString();
return Json(new { success = true, result });
}
catch (Exception exception)
{
//logowanie
return Json(new { success = false, message = exception.Message });
}
}
}
}
PODSUMOWANIE
Pokazałem Ci w tym materiale sporo nowości. Użyliśmy JavaScript, jQuery, ajax'a i oczywiście C#, dzięki czemu udało nam się napisać całkiem fajną aplikację webową w ASP.NET Core. Oczywiście taką aplikację możesz jeszcze dowolnie rozwijać, na przykład o nowe operacje. Jeżeli chcesz, to możesz to zrobić i stworzyć jeszcze bardziej zaawansowany kalkulator.
Jeżeli taki artykuł Ci się spodobał, to koniecznie dołącz do mojej społeczności. Zapisz się na darmowy newsletter, gdzie co tydzień dzielę się wartościowymi materiałami w szczególności dotyczącymi C# i platformy .NET (darmowy zapis – newsletter).
Poprzedni artykuł - Pierwsza Aplikacja Webowa ASP.NET Core w C# – UI w Razor (1/2).
Następny artykuł - Pierwsza Aplikacja Mobilna Xamarin w C# – UI w XAML (1/2).