Todo list in AngularJS e .NET MVC
Questa è la versione .NET MVC dell'applicazione Todo list in AngularJS e NodeJs.
Lato client ( AngularJS 1 ) non cambia niente, quello che cambia sono le API lato server in quanto sono implementate in Web API MVC.
La struttura delle cartelle dell'applicazione è questa:
Rispetto alla versione NodeJs, essendo C# tipizzato, ho dovuto creare una serie di oggetti che rappresentano i contenuti JSON inviati dalle Web API (/Models/BO).
Come mia regola tendo a intercettare tutti gli errori possibili in modo che le Web API non sollevino mai eccezioni difficilmente gestibili lato client in JavaScript. Quindi tutte le risposte sono "wrappate" in un oggetto ServiceStatus:
la proprietà Data, di tipo "generics", conterrà il tipo di oggetto in risposta.
Il metodo delle Web API sarà sempre fatto secondo questo modello:
Perché tutto funzioni senza modificare la app AngularJS devo fare in modo che le Web API rispondano secondo gli standard JavaScript ovvero in notazione camleCase. Posso ottenere questo aggiungendo del codice nel metodo Application_Start del Global.asax:
In questa versione ho apportato una piccola modifica lato AngularJS per fare in modo che le url del browser non contengano il carattere hash (#).
Questo lo si ottiene agendo sul file nel file /app/others/app-route.js (solo sui browser moderni):
e impostando il tag base nell'head della pagina /Views/home/Index.cshtml:
L'ultima modifica che ho apportato è a livello grafico, agendo solo sul CSS, per dare un diverso feedback all'utente durante le chiamate alle API:
Il codice sorgente può essere scaricato da qui Download SgarMvcAngularJS o da GitHub. E può essere compilato e modificato con Visual Studio 2015 Community (versione gratuita). Se non funziona aggiorna i pacchetti NuGet.
Per funzionare è necessario che sulla macchina sia presente un database Microsoft SQL Server.
Fatto questo è necessario creare il database tramite lo script /App_data/schema.sql ed eventualmente cambiare i parametri di configurazione in nel Web.config
05/10/2019 Aggiunto i progetti su GitHub:
Lato client ( AngularJS 1 ) non cambia niente, quello che cambia sono le API lato server in quanto sono implementate in Web API MVC.
La struttura delle cartelle dell'applicazione è questa:
Text
/
app ... l'applicazione SPA AngularJS 1
controllers ... tutti i controllers dell'applicazione
modal ... i controllers delle pagine modali
directives ... le eventuali direttive
others ... l'entry point della applicazione
pages ... le pagine dell'applicazione
modal ... le pagine modali
services ... i service/factory che si occupano dell'accesso ai dati
app.js ... entry point angular js dove viene definita la APP
App_Data ... contiene lo script sql
App_Start ... inizializzatori MVC
RouteConfig.cs ... viene ridefinita la route di default per usare le url senza hash #
Code
Manager.cs ... la classe che accede al DB (per semplicità di porting da NodeJS ho usato ADO.NET)
Contents
css ... i css di bootstra e quelli custo dell'applicazione
fonts ... i font di bootstrap
images ... eventuali immagini
js ... javscript librerie AngularJS
Controllers ... le pagine js lato server di nodejs (pagine e API)
HomeController.js ... il controller dell'unica pagina C# MVC
TodoController.js ... le API C# MVC
Models
BO ... gli oggetti usati dalle Web API
Views
Home
Index.html ... l'unica pagina C# MVC che funge da "Master Page"
Global.asax ... dove viene impostata la formattazione JSON camelCase delle Web API
Web.config ... dove è definita la connection string al DB
Come mia regola tendo a intercettare tutti gli errori possibili in modo che le Web API non sollevino mai eccezioni difficilmente gestibili lato client in JavaScript. Quindi tutte le risposte sono "wrappate" in un oggetto ServiceStatus:
C#
using System;
using System.Collections.Generic;
namespace Sgart.MvcAngularJS.Models.BO
{
public class ServiceStatus
{
private const int SUCCESS_SECONDS = 2;
public ServiceStatus()
{
Success = false;
Messages = new List<ServiceStatusErrorItem>();
ReturnValue = -1;
ErrorCount = 0;
}
public bool Success { get; set; }
public List<ServiceStatusErrorItem> Messages { get; set; }
public object ReturnValue { get; set; }
public int ErrorCount { get; set; }
public void AddError(string message, int seconds = 0)
{
Messages.Add(new ServiceStatusErrorItem(ServiceStatusErrorType.Error, message, seconds));
Success = false;
ErrorCount++;
}
public void AddError(Exception ex, int seconds = 0)
{
Messages.Add(new ServiceStatusErrorItem(ServiceStatusErrorType.Error, ex.Message, seconds));
Success = false;
ErrorCount++;
}
public void AddWarning(string message, int seconds = 0)
{
Messages.Add(new ServiceStatusErrorItem(ServiceStatusErrorType.Warning, message, seconds));
}
public void AddWarning(Exception ex, int seconds = 0)
{
Messages.Add(new ServiceStatusErrorItem(ServiceStatusErrorType.Warning, ex.Message, seconds));
}
public void AddSuccess(string message, int seconds = SUCCESS_SECONDS)
{
Messages.Add(new ServiceStatusErrorItem(ServiceStatusErrorType.Success, message, seconds));
}
public void AddInfo(string message, int seconds = 0)
{
Messages.Add(new ServiceStatusErrorItem(ServiceStatusErrorType.Info, message, seconds));
}
}
public class ServiceStatus<T> : ServiceStatus
{
public T Data { get; set; }
}
public class ServiceStatusList<T> : ServiceStatus<List<T>>
{
public ServiceStatusList() : base()
{
}
}
}
Il metodo delle Web API sarà sempre fatto secondo questo modello:
C#
[HttpGet]
[Route("todo/search")]
public BO.ServiceStatusList<BO.TodoItem> Search([FromUri] BO.SearchInputItem param)
{
BO.ServiceStatusList<BO.TodoItem> result = new Models.BO.ServiceStatusList<Models.BO.TodoItem>();
result.Data = new List<Models.BO.TodoItem>();
try
{
result.Data = Metodo.Carica.Dati.Da.DB ...;
result.AddSuccess("eventuale messaggio di successo");
result.Success = true; // settare a true se tutto OK
}
catch (Exception ex)
{
result.AddError(ex); // messaggio di errore, success=false
}
return result;
}
C#
protected void Application_Start()
{
...
// forzo la formattazione dei nomi in stile JavaScript, camelCase
var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
Questo lo si ottiene agendo sul file nel file /app/others/app-route.js (solo sui browser moderni):
JavaScript
// configure html5 to get links working
// you URLs will be sgart.it/home rather than sgart.it/#/home
$locationProvider.html5Mode({ enabled: true });
HTML
<!DOCTYPE html>
<html data-ng-app="app" data-ng-cloak>
<head>
...
<!-- base: necessario ad angular per gestire i path senza hash # -->
<base href="/">
...
</head>
Il codice sorgente può essere scaricato da qui Download SgarMvcAngularJS o da GitHub. E può essere compilato e modificato con Visual Studio 2015 Community (versione gratuita). Se non funziona aggiorna i pacchetti NuGet.
Per funzionare è necessario che sulla macchina sia presente un database Microsoft SQL Server.
Fatto questo è necessario creare il database tramite lo script /App_data/schema.sql ed eventualmente cambiare i parametri di configurazione in nel Web.config
05/10/2019 Aggiunto i progetti su GitHub:
- Versione in Net.Core 3 e Angular 1 / JS => SgartCore3Angular1Todo
- Versione in Net.Core 3, Entity Framework 6 e Angular 1 / JS => SgartCore3Ef6Angular1Todo