mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-16 00:05:53 +01:00
Merge branch 'master' of https://github.com/danieleteti/delphimvcframework into HEAD
This commit is contained in:
commit
18e4412f73
28
README.md
28
README.md
@ -32,6 +32,7 @@
|
|||||||
* Messaging extension using STOMP (beta)
|
* Messaging extension using STOMP (beta)
|
||||||
* Automatic documentation through /system/describeserver.info
|
* Automatic documentation through /system/describeserver.info
|
||||||
* Driven by its huge community (Facebook group https://www.facebook.com/groups/delphimvcframework)
|
* Driven by its huge community (Facebook group https://www.facebook.com/groups/delphimvcframework)
|
||||||
|
* Semantic Versioning
|
||||||
* Simple and [documented](https://github.com/danieleteti/delphimvcframework/blob/master/docs/ITDevCON%202013%20-%20Introduction%20to%20DelphiMVCFramework.pdf)
|
* Simple and [documented](https://github.com/danieleteti/delphimvcframework/blob/master/docs/ITDevCON%202013%20-%20Introduction%20to%20DelphiMVCFramework.pdf)
|
||||||
* Check the [DMVCFramework Developer Guide](https://danieleteti.gitbooks.io/delphimvcframework/content/) (work in progress)
|
* Check the [DMVCFramework Developer Guide](https://danieleteti.gitbooks.io/delphimvcframework/content/) (work in progress)
|
||||||
|
|
||||||
@ -56,6 +57,31 @@ These are the most notable:
|
|||||||
* DelphiRedisClient (https://github.com/danieleteti/delphiredisclient)
|
* DelphiRedisClient (https://github.com/danieleteti/delphiredisclient)
|
||||||
* LoggerPro (https://github.com/danieleteti/loggerpro)
|
* LoggerPro (https://github.com/danieleteti/loggerpro)
|
||||||
|
|
||||||
|
### Using ObjectsMappers in Delphi Starter Edition
|
||||||
|
A lot of users ask about it, now is possible to use the Mapper also in Delphi Started Edition. To enable the "StarterEditionMode" open ```sources\dmvcframework.inc``` and remove the dot (.) after the curly brace in the following line
|
||||||
|
|
||||||
|
```{.$DEFINE STARTEREDITION}```
|
||||||
|
|
||||||
|
become
|
||||||
|
|
||||||
|
```{$DEFINE STARTEREDITION}```
|
||||||
|
|
||||||
|
## Release Notes
|
||||||
|
**2.1.3 (lithium)**
|
||||||
|
- FIX https://github.com/danieleteti/delphimvcframework/issues/64
|
||||||
|
- Added unit tests to avoid regressions
|
||||||
|
|
||||||
|
**2.1.2 (helium)**
|
||||||
|
- FIX for Delphi versions who don't have ```TJSONBool``` (Delphi XE8 or older)
|
||||||
|
- Added new conditional define in dmvcframework.inc: JSONBOOL (defined for Delphi Seattle+)
|
||||||
|
|
||||||
|
**2.1.1 (hydrogen)**
|
||||||
|
- Updated the IDE Expert to show the current version of the framework
|
||||||
|
- FIX to the mapper about the datasets null values (needs to be checked in old Delphi versions)
|
||||||
|
- ADDED support for boolean values in datasets serialization
|
||||||
|
- ADDED unit tests about Mapper and dataset fields nullability
|
||||||
|
- The current version is available in constant ```DMVCFRAMEWORK_VERSION``` defined in ```MVCFramework.Commons.pas```
|
||||||
|
|
||||||
##Samples and documentation
|
##Samples and documentation
|
||||||
DMVCFramework is provided with a lot of examples focused on specific functionality.
|
DMVCFramework is provided with a lot of examples focused on specific functionality.
|
||||||
All samples are in [Samples](https://github.com/danieleteti/delphimvcframework/tree/master/samples) folder.
|
All samples are in [Samples](https://github.com/danieleteti/delphimvcframework/tree/master/samples) folder.
|
||||||
@ -66,7 +92,7 @@ Check the [DMVCFramework Developer Guide](https://danieleteti.gitbooks.io/delphi
|
|||||||
Below the is a basic sample of a DMVCFramework server wich can be deployed as standa-alone application, as an Apache module or as ISAPI dll. This flexibility is provided by the Delphi WebBroker framework (built-in in Delphi since Delphi 4).
|
Below the is a basic sample of a DMVCFramework server wich can be deployed as standa-alone application, as an Apache module or as ISAPI dll. This flexibility is provided by the Delphi WebBroker framework (built-in in Delphi since Delphi 4).
|
||||||
The project containes an IDE Expert which make creating DMVCFramework project a breeze. However not all the Delphi version are supported, so here's the manual version (which is not complicated at all).
|
The project containes an IDE Expert which make creating DMVCFramework project a breeze. However not all the Delphi version are supported, so here's the manual version (which is not complicated at all).
|
||||||
|
|
||||||
To create this server, you have to create a new Delphi Projects -> WebBroker -> WebServerApplication. Then add the following changes to the webmodule.
|
To create this server, you have to create a new ```Delphi Projects -> WebBroker -> WebServerApplication```. Then add the following changes to the webmodule.
|
||||||
```delphi
|
```delphi
|
||||||
unit WebModuleUnit1;
|
unit WebModuleUnit1;
|
||||||
|
|
||||||
|
24
build.py
24
build.py
@ -8,7 +8,7 @@ init()
|
|||||||
|
|
||||||
|
|
||||||
#################################################################################
|
#################################################################################
|
||||||
def buildProject(project):
|
def buildProject(project, platform = 'Win32'):
|
||||||
print(Fore.YELLOW + "Building " + project)
|
print(Fore.YELLOW + "Building " + project)
|
||||||
p = project.replace('.dproj', '.cfg')
|
p = project.replace('.dproj', '.cfg')
|
||||||
if os.path.isfile(p):
|
if os.path.isfile(p):
|
||||||
@ -16,7 +16,7 @@ def buildProject(project):
|
|||||||
os.remove(p + '.unused')
|
os.remove(p + '.unused')
|
||||||
os.rename(p, p + '.unused')
|
os.rename(p, p + '.unused')
|
||||||
# print os.system("msbuild /t:Build /p:Config=Debug \"" + project + "\"")
|
# print os.system("msbuild /t:Build /p:Config=Debug \"" + project + "\"")
|
||||||
return subprocess.call("rsvars.bat & msbuild /t:Build /p:Config=Debug /p:Platform=Win32 \"" + project + "\"", shell=True) == 0
|
return subprocess.call("rsvars.bat & msbuild /t:Build /p:Config=Debug /p:Platform=" + platform + " \"" + project + "\"", shell=True) == 0
|
||||||
|
|
||||||
|
|
||||||
def summaryTable(builds):
|
def summaryTable(builds):
|
||||||
@ -26,19 +26,14 @@ def summaryTable(builds):
|
|||||||
print(Fore.YELLOW + "=" * 90)
|
print(Fore.YELLOW + "=" * 90)
|
||||||
good = bad = 0
|
good = bad = 0
|
||||||
for item in builds:
|
for item in builds:
|
||||||
if item['status'] == 'ok':
|
if item['status'].startswith('ok'):
|
||||||
#WConio.textcolor(WConio.LIGHTGREEN)
|
|
||||||
good += 1
|
good += 1
|
||||||
else:
|
else:
|
||||||
#WConio.textcolor(WConio.RED)
|
|
||||||
bad += 1
|
bad += 1
|
||||||
print(Fore.BLUE + item['project'].ljust(80) + (Fore.WHITE if item['status'] == 'ok' else Fore.RED) + item['status'].ljust(4))
|
print(Fore.BLUE + item['project'].ljust(80) + (Fore.WHITE if item['status'].startswith('ok') else Fore.RED) + item['status'].ljust(4))
|
||||||
|
|
||||||
#WConio.textcolor(WConio.WHITE)
|
|
||||||
print(Fore.YELLOW + "=" * 90)
|
print(Fore.YELLOW + "=" * 90)
|
||||||
#WConio.textcolor(WConio.GREEN)
|
|
||||||
print(Fore.WHITE + "GOOD :".rjust(80) + str(good).rjust(10, '.'))
|
print(Fore.WHITE + "GOOD :".rjust(80) + str(good).rjust(10, '.'))
|
||||||
#WConio.textcolor(WConio.RED)
|
|
||||||
print(Fore.RED + "BAD :".rjust(80) + str(bad).rjust(10, '.'))
|
print(Fore.RED + "BAD :".rjust(80) + str(bad).rjust(10, '.'))
|
||||||
|
|
||||||
|
|
||||||
@ -49,6 +44,7 @@ def main(projects):
|
|||||||
builds = []
|
builds = []
|
||||||
for project in projects:
|
for project in projects:
|
||||||
filename = '\\'.join(project.split('\\')[-3:])
|
filename = '\\'.join(project.split('\\')[-3:])
|
||||||
|
list = {'project': filename}
|
||||||
if project.find('delphistompclient') > -1 or project.find('contribsamples') > -1:
|
if project.find('delphistompclient') > -1 or project.find('contribsamples') > -1:
|
||||||
list['status'] = 'skip'
|
list['status'] = 'skip'
|
||||||
continue
|
continue
|
||||||
@ -59,6 +55,14 @@ def main(projects):
|
|||||||
else:
|
else:
|
||||||
list["status"] = "ko"
|
list["status"] = "ko"
|
||||||
builds.append(list)
|
builds.append(list)
|
||||||
|
|
||||||
|
if (os.path.exists(project + '.android')):
|
||||||
|
list = {'project': filename}
|
||||||
|
if buildProject(project, 'Android'):
|
||||||
|
list["status"] = "okandroid"
|
||||||
|
else:
|
||||||
|
list["status"] = "koandroid"
|
||||||
|
builds.append(list)
|
||||||
summaryTable(builds)
|
summaryTable(builds)
|
||||||
|
|
||||||
# Store current attribute settings
|
# Store current attribute settings
|
||||||
@ -67,7 +71,7 @@ def main(projects):
|
|||||||
def dmvc_copyright():
|
def dmvc_copyright():
|
||||||
print(Style.BRIGHT + Fore.WHITE + "----------------------------------------------------------------------------------------")
|
print(Style.BRIGHT + Fore.WHITE + "----------------------------------------------------------------------------------------")
|
||||||
print(Fore.RED + " ** Delphi MVC Framework Building System **")
|
print(Fore.RED + " ** Delphi MVC Framework Building System **")
|
||||||
print(Fore.WHITE + "Delphi MVC Framework is CopyRight (2010-2016) of Daniele Teti and the DMVCFramework TEAM")
|
print(Fore.WHITE + "Delphi MVC Framework is CopyRight (2010-2017) of Daniele Teti and the DMVCFramework TEAM")
|
||||||
print(Fore.RESET + "----------------------------------------------------------------------------------------\n")
|
print(Fore.RESET + "----------------------------------------------------------------------------------------\n")
|
||||||
|
|
||||||
## MAIN ##
|
## MAIN ##
|
||||||
|
BIN
docs/periodic_table.png
Normal file
BIN
docs/periodic_table.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 MiB |
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
{ *************************************************************************** }
|
// ***************************************************************************
|
||||||
{ }
|
//
|
||||||
{ Delphi MVC Framework }
|
// Delphi MVC Framework
|
||||||
{ }
|
//
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
|
||||||
{ }
|
//
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
// https://github.com/danieleteti/delphimvcframework
|
||||||
{ }
|
//
|
||||||
{ *************************************************************************** }
|
// ***************************************************************************
|
||||||
{ }
|
//
|
||||||
{ Licensed under the Apache License, Version 2.0 (the "License"); }
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
{ you may not use this file except in compliance with the License. }
|
// you may not use this file except in compliance with the License.
|
||||||
{ You may obtain a copy of the License at }
|
// You may obtain a copy of the License at
|
||||||
{ }
|
//
|
||||||
{ http://www.apache.org/licenses/LICENSE-2.0 }
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
{ }
|
//
|
||||||
{ Unless required by applicable law or agreed to in writing, software }
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
{ distributed under the License is distributed on an "AS IS" BASIS, }
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
{ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
{ See the License for the specific language governing permissions and }
|
// See the License for the specific language governing permissions and
|
||||||
{ limitations under the License. }
|
// limitations under the License.
|
||||||
{ }
|
//
|
||||||
{ This IDE expert is based off of the one included with the DUnitX }
|
// This IDE expert is based off of the one included with the DUnitX
|
||||||
{ project. Original source by Robert Love. Adapted by Nick Hodges. }
|
// project. Original source by Robert Love. Adapted by Nick Hodges.
|
||||||
{ }
|
//
|
||||||
{ The DUnitX project is run by Vincent Parrett and can be found at: }
|
// The DUnitX project is run by Vincent Parrett and can be found at:
|
||||||
{ }
|
//
|
||||||
{ https://github.com/VSoftTechnologies/DUnitX }
|
// https://github.com/VSoftTechnologies/DUnitX
|
||||||
{ *************************************************************************** }
|
// ***************************************************************************
|
||||||
|
|
||||||
unit DMVC.Expert.CodeGen.Templates;
|
unit DMVC.Expert.CodeGen.Templates;
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ resourcestring
|
|||||||
'uses' + sLineBreak +
|
'uses' + sLineBreak +
|
||||||
' System.SysUtils,' + sLineBreak +
|
' System.SysUtils,' + sLineBreak +
|
||||||
' MVCFramework.Logger,' + sLineBreak +
|
' MVCFramework.Logger,' + sLineBreak +
|
||||||
|
' MVCFramework.Commons,' + sLineBreak +
|
||||||
' Winapi.Windows,' + sLineBreak +
|
' Winapi.Windows,' + sLineBreak +
|
||||||
' Winapi.ShellAPI,' + sLineBreak +
|
' Winapi.ShellAPI,' + sLineBreak +
|
||||||
' ReqMulti, {enables files upload}' + sLineBreak +
|
' ReqMulti, {enables files upload}' + sLineBreak +
|
||||||
@ -60,7 +61,7 @@ resourcestring
|
|||||||
' LHandle: THandle;' + sLineBreak +
|
' LHandle: THandle;' + sLineBreak +
|
||||||
' LServer: TIdHTTPWebBrokerBridge;' + sLineBreak +
|
' LServer: TIdHTTPWebBrokerBridge;' + sLineBreak +
|
||||||
'begin' + sLineBreak +
|
'begin' + sLineBreak +
|
||||||
' Writeln(''** DMVCFramework Server **'');' + sLineBreak +
|
' Writeln(''** DMVCFramework Server ** build '' + DMVCFRAMEWORK_VERSION);' + sLineBreak +
|
||||||
' Writeln(Format(''Starting HTTP Server on port %%d'', [APort]));' + sLineBreak +
|
' Writeln(Format(''Starting HTTP Server on port %%d'', [APort]));' + sLineBreak +
|
||||||
' LServer := TIdHTTPWebBrokerBridge.Create(nil);' + sLineBreak +
|
' LServer := TIdHTTPWebBrokerBridge.Create(nil);' + sLineBreak +
|
||||||
' try' + sLineBreak +
|
' try' + sLineBreak +
|
||||||
@ -95,6 +96,7 @@ resourcestring
|
|||||||
sLineBreak +
|
sLineBreak +
|
||||||
'begin' + sLineBreak +
|
'begin' + sLineBreak +
|
||||||
' ReportMemoryLeaksOnShutdown := True;' + sLineBreak +
|
' ReportMemoryLeaksOnShutdown := True;' + sLineBreak +
|
||||||
|
' IsMultiThread := True;' + sLineBreak +
|
||||||
' try' + sLineBreak +
|
' try' + sLineBreak +
|
||||||
' if WebRequestHandler <> nil then' + sLineBreak +
|
' if WebRequestHandler <> nil then' + sLineBreak +
|
||||||
' WebRequestHandler.WebModuleClass := WebModuleClass;' + sLineBreak +
|
' WebRequestHandler.WebModuleClass := WebModuleClass;' + sLineBreak +
|
||||||
@ -117,7 +119,7 @@ resourcestring
|
|||||||
'interface' + sLineBreak +
|
'interface' + sLineBreak +
|
||||||
sLineBreak +
|
sLineBreak +
|
||||||
'uses' + sLineBreak +
|
'uses' + sLineBreak +
|
||||||
' MVCFramework;' + sLineBreak +
|
' MVCFramework, MVCFramework.Commons;' + sLineBreak +
|
||||||
sLineBreak +
|
sLineBreak +
|
||||||
'type' + sLineBreak +
|
'type' + sLineBreak +
|
||||||
sLineBreak +
|
sLineBreak +
|
||||||
@ -128,7 +130,7 @@ resourcestring
|
|||||||
'%4:s' +
|
'%4:s' +
|
||||||
' end;' + sLineBreak +
|
' end;' + sLineBreak +
|
||||||
sLineBreak +
|
sLineBreak +
|
||||||
'implementation' + sLineBreak +
|
'implementation' + sLineBreak + sLineBreak +
|
||||||
'uses' + sLineBreak +
|
'uses' + sLineBreak +
|
||||||
' MVCFramework.Logger;' + sLineBreak +
|
' MVCFramework.Logger;' + sLineBreak +
|
||||||
sLineBreak +
|
sLineBreak +
|
||||||
@ -150,16 +152,15 @@ resourcestring
|
|||||||
'procedure %0:s.Index;' + sLineBreak +
|
'procedure %0:s.Index;' + sLineBreak +
|
||||||
'begin' + sLineBreak +
|
'begin' + sLineBreak +
|
||||||
' //use Context property to access to the HTTP request and response ' + sLineBreak +
|
' //use Context property to access to the HTTP request and response ' + sLineBreak +
|
||||||
' Render(''Hello World'');' + sLineBreak +
|
' Render(''Hello DelphiMVCFramework World'');' + sLineBreak +
|
||||||
sLineBreak +
|
|
||||||
'end;' + sLineBreak + sLineBreak +
|
'end;' + sLineBreak + sLineBreak +
|
||||||
'procedure %0:s.GetSpecializedHello(const FirstName: String);' + sLineBreak +
|
'procedure %0:s.GetSpecializedHello(const FirstName: String);' + sLineBreak +
|
||||||
'begin' + sLineBreak +
|
'begin' + sLineBreak +
|
||||||
' Render(''Hello '' + FirstName);' + sLineBreak +
|
' Render(''Hello '' + FirstName);' + sLineBreak +
|
||||||
sLineBreak +
|
|
||||||
'end;' + sLineBreak;
|
'end;' + sLineBreak;
|
||||||
|
|
||||||
sActionFiltersIntf =
|
sActionFiltersIntf =
|
||||||
|
' protected' + sLineBreak +
|
||||||
' procedure OnBeforeAction(Context: TWebContext; const AActionName: string; var Handled: Boolean); override;'
|
' procedure OnBeforeAction(Context: TWebContext; const AActionName: string; var Handled: Boolean); override;'
|
||||||
+ sLineBreak +
|
+ sLineBreak +
|
||||||
' procedure OnAfterAction(Context: TWebContext; const AActionName: string); override;' +
|
' procedure OnAfterAction(Context: TWebContext; const AActionName: string); override;' +
|
||||||
@ -244,7 +245,8 @@ resourcestring
|
|||||||
' Config[TMVCConfigKey.Messaging] := ''false'';' + sLineBreak +
|
' Config[TMVCConfigKey.Messaging] := ''false'';' + sLineBreak +
|
||||||
' //Enable Server Signature in response' + sLineBreak +
|
' //Enable Server Signature in response' + sLineBreak +
|
||||||
' Config[TMVCConfigKey.ExposeServerSignature] := ''true'';' + sLineBreak +
|
' Config[TMVCConfigKey.ExposeServerSignature] := ''true'';' + sLineBreak +
|
||||||
' // Define a default URL for requests that don''t map to a route or a file (useful for client side web app)' + sLineBreak +
|
' // Define a default URL for requests that don''t map to a route or a file (useful for client side web app)' +
|
||||||
|
sLineBreak +
|
||||||
' Config[TMVCConfigKey.FallbackResource] := ''index.html'';' + sLineBreak +
|
' Config[TMVCConfigKey.FallbackResource] := ''index.html'';' + sLineBreak +
|
||||||
' end);' + sLineBreak +
|
' end);' + sLineBreak +
|
||||||
' FMVC.AddController(%3:s);' + sLineBreak +
|
' FMVC.AddController(%3:s);' + sLineBreak +
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{ }
|
{ }
|
||||||
{ Delphi MVC Framework }
|
{ Delphi MVC Framework }
|
||||||
{ }
|
{ }
|
||||||
{ Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team }
|
{ Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team }
|
||||||
{ }
|
{ }
|
||||||
{ https://github.com/danieleteti/delphimvcframework }
|
{ https://github.com/danieleteti/delphimvcframework }
|
||||||
{ }
|
{ }
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 31001af0cb56097b4a982d75c9089693a166863e
|
Subproject commit 6724ff46bbfae41129e5c54bd4a7fc991de55129
|
@ -1 +1 @@
|
|||||||
Subproject commit a4a9abd311152797dff16886d81b77678fd0b57c
|
Subproject commit cd36a96d19c983f3b59d27e9d4d127feb9f297ac
|
@ -1 +1 @@
|
|||||||
Subproject commit 7e84f870082d60c94c3ffe7a930c8be847f9fc2c
|
Subproject commit 765512249408358eacf2e07dd39eec1b2a653ba3
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Delphi MVC Framework
|
// Delphi MVC Framework
|
||||||
//
|
//
|
||||||
// Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team
|
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
|
||||||
//
|
//
|
||||||
// https://github.com/danieleteti/delphimvcframework
|
// https://github.com/danieleteti/delphimvcframework
|
||||||
//
|
//
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Delphi MVC Framework
|
// Delphi MVC Framework
|
||||||
//
|
//
|
||||||
// Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team
|
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
|
||||||
//
|
//
|
||||||
// https://github.com/danieleteti/delphimvcframework
|
// https://github.com/danieleteti/delphimvcframework
|
||||||
//
|
//
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Delphi MVC Framework
|
// Delphi MVC Framework
|
||||||
//
|
//
|
||||||
// Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team
|
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
|
||||||
//
|
//
|
||||||
// https://github.com/danieleteti/delphimvcframework
|
// https://github.com/danieleteti/delphimvcframework
|
||||||
//
|
//
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Delphi MVC Framework
|
// Delphi MVC Framework
|
||||||
//
|
//
|
||||||
// Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team
|
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
|
||||||
//
|
//
|
||||||
// https://github.com/danieleteti/delphimvcframework
|
// https://github.com/danieleteti/delphimvcframework
|
||||||
//
|
//
|
||||||
@ -27,7 +27,7 @@ unit PrivateControllerU;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
MVCFramework;
|
MVCFramework, MVCFramework.Commons;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Delphi MVC Framework
|
// Delphi MVC Framework
|
||||||
//
|
//
|
||||||
// Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team
|
// Copyright (c) 2010-2017 Daniele Teti and the DMVCFramework Team
|
||||||
//
|
//
|
||||||
// https://github.com/danieleteti/delphimvcframework
|
// https://github.com/danieleteti/delphimvcframework
|
||||||
//
|
//
|
||||||
@ -27,7 +27,7 @@ unit PublicControllerU;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
MVCFramework;
|
MVCFramework, MVCFramework.Commons;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ unit CustomersControllerU;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
MVCFramework, CustomersTDGU;
|
MVCFramework, MVCFramework.Commons, CustomersTDGU;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
|
Binary file not shown.
@ -68,7 +68,7 @@ procedure TWineCellarDataModule.ConnectionBeforeConnect(Sender: TObject);
|
|||||||
begin
|
begin
|
||||||
Connection.Params.Values['Database'] :=
|
Connection.Params.Values['Database'] :=
|
||||||
{ change this path to be compliant with your system }
|
{ change this path to be compliant with your system }
|
||||||
'C:\DEV\DMVCFramework\samples\winecellar\WINES.FDB';
|
'D:\DEV\dmvcframework\samples\winecellarserver\WINES.FDB';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TWineCellarDataModule.FindWines(Search: string): TJSONArray;
|
function TWineCellarDataModule.FindWines(Search: string): TJSONArray;
|
||||||
|
@ -4,6 +4,7 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
MVCFramework,
|
MVCFramework,
|
||||||
|
MVCFramework.Commons,
|
||||||
MainDataModuleUnit;
|
MainDataModuleUnit;
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -52,7 +53,7 @@ type
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.SysUtils, System.Classes, System.IOUtils, MVCFramework.Commons;
|
System.SysUtils, System.Classes, System.IOUtils;
|
||||||
|
|
||||||
procedure TWineCellarApp.FindWines(ctx: TWebContext);
|
procedure TWineCellarApp.FindWines(ctx: TWebContext);
|
||||||
begin
|
begin
|
||||||
|
@ -70,8 +70,8 @@ end;
|
|||||||
procedure TArticle.CheckDelete;
|
procedure TArticle.CheckDelete;
|
||||||
begin
|
begin
|
||||||
inherited;
|
inherited;
|
||||||
if Price > 0 then
|
// if Price > 0 then
|
||||||
raise Exception.Create('Cannot delete an article with a price greater than 0 (yes, it is a silly check)');
|
// raise Exception.Create('Cannot delete an article with a price greater than 0 (yes, it is a silly check)');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TArticle.CheckInsert;
|
procedure TArticle.CheckInsert;
|
||||||
|
@ -2,7 +2,7 @@ unit Controllers.Articles;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses mvcframework, Controllers.Base;
|
uses mvcframework, mvcframework.Commons, Controllers.Base;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -13,8 +13,8 @@ type
|
|||||||
[MVCDoc('Returns the list of articles')]
|
[MVCDoc('Returns the list of articles')]
|
||||||
[MVCPath]
|
[MVCPath]
|
||||||
[MVCHTTPMethod([httpGET])]
|
[MVCHTTPMethod([httpGET])]
|
||||||
|
|
||||||
procedure GetArticles;
|
procedure GetArticles;
|
||||||
|
|
||||||
[MVCDoc('Returns the article with the specified id')]
|
[MVCDoc('Returns the article with the specified id')]
|
||||||
[MVCPath('/($id)')]
|
[MVCPath('/($id)')]
|
||||||
[MVCHTTPMethod([httpGET])]
|
[MVCHTTPMethod([httpGET])]
|
||||||
@ -40,7 +40,7 @@ implementation
|
|||||||
|
|
||||||
{ TArticlesController }
|
{ TArticlesController }
|
||||||
|
|
||||||
uses Services, BusinessObjects, Commons, mvcframework.Commons;
|
uses Services, BusinessObjects, Commons;
|
||||||
|
|
||||||
procedure TArticlesController.CreateArticle(Context: TWebContext);
|
procedure TArticlesController.CreateArticle(Context: TWebContext);
|
||||||
var
|
var
|
||||||
|
@ -3,7 +3,7 @@ unit Controllers.Base;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
MVCFramework, Services, MainDM;
|
MVCFramework, MVCFramework.Commons, Services, MainDM;
|
||||||
|
|
||||||
type
|
type
|
||||||
TBaseController = class abstract(TMVCController)
|
TBaseController = class abstract(TMVCController)
|
||||||
|
@ -22,7 +22,7 @@ implementation
|
|||||||
|
|
||||||
{ %CLASSGROUP 'Vcl.Controls.TControl' }
|
{ %CLASSGROUP 'Vcl.Controls.TControl' }
|
||||||
|
|
||||||
uses Controllers.Articles;
|
uses Controllers.Articles, MVCFramework.Middleware.CORS;
|
||||||
|
|
||||||
{$R *.dfm}
|
{$R *.dfm}
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ procedure TWebModule1.WebModuleCreate(Sender: TObject);
|
|||||||
begin
|
begin
|
||||||
FEngine := TMVCEngine.Create(self);
|
FEngine := TMVCEngine.Create(self);
|
||||||
FEngine.AddController(TArticlesController);
|
FEngine.AddController(TArticlesController);
|
||||||
|
FEngine.AddMiddleware(TCORSMiddleware.Create);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -157,7 +157,7 @@
|
|||||||
<iPad_SpotLight80>$(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png</iPad_SpotLight80>
|
<iPad_SpotLight80>$(BDS)\bin\Artwork\iOS\iPad\FM_SpotlightSearchIcon_80x80.png</iPad_SpotLight80>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
<PropertyGroup Condition="'$(Base_Win32)'!=''">
|
||||||
<DCC_UnitSearchPath>..\..\lib\iocpdelphiframework\Base;..\..\lib\delphistompclient;..\..\lib\luadelphibinding;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
|
<DCC_UnitSearchPath>..\..\lib\iocpdelphiframework\Base;..\..\lib\luadelphibinding;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
|
||||||
<DCC_ExeOutput>bin</DCC_ExeOutput>
|
<DCC_ExeOutput>bin</DCC_ExeOutput>
|
||||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||||
<DCC_UsePackage>FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;SampleListViewMultiDetailAppearancePackage;FireDACPgDriver;fmx;IndySystem;TeeDB;tethering;ITDevCon2012AdapterPackage;inetdbbde;vclib;DBXInterBaseDriver;DataSnapClient;DataSnapServer;DataSnapCommon;DataSnapProviderClient;DBXSybaseASEDriver;DbxCommonDriver;vclimg;dbxcds;DatasnapConnectorsFreePascal;MetropolisUILiveTile;vcldb;vcldsnap;fmxFireDAC;DBXDb2Driver;DBXOracleDriver;CustomIPTransport;vclribbon;dsnap;IndyIPServer;fmxase;vcl;IndyCore;DBXMSSQLDriver;IndyIPCommon;CloudService;FmxTeeUI;FireDACIBDriver;CodeSiteExpressPkg;DataSnapFireDAC;FireDACDBXDriver;soapserver;inetdbxpress;dsnapxml;FireDACInfxDriver;FireDACDb2Driver;adortl;CustomAdaptersMDPackage;FireDACASADriver;bindcompfmx;vcldbx;FireDACODBCDriver;RESTBackendComponents;rtl;dbrtl;DbxClientDriver;FireDACCommon;bindcomp;inetdb;SampleListViewRatingsAppearancePackage;Tee;DBXOdbcDriver;vclFireDAC;xmlrtl;DataSnapNativeClient;svnui;ibxpress;IndyProtocols;DBXMySQLDriver;FireDACCommonDriver;bindcompdbx;bindengine;vclactnband;soaprtl;FMXTee;TeeUI;bindcompvcl;vclie;FireDACADSDriver;vcltouch;VclSmp;FireDACMSSQLDriver;FireDAC;DBXInformixDriver;Intraweb;VCLRESTComponents;DataSnapConnectors;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;SampleGenerator1Package;inet;fmxobj;FireDACMySQLDriver;soapmidas;vclx;svn;DBXSybaseASADriver;FireDACOracleDriver;fmxdae;RESTComponents;bdertl;FireDACMSAccDriver;dbexpress;DataSnapIndy10ServerTransport;IndyIPClient;$(DCC_UsePackage)</DCC_UsePackage>
|
<DCC_UsePackage>FireDACSqliteDriver;FireDACDSDriver;DBXSqliteDriver;SampleListViewMultiDetailAppearancePackage;FireDACPgDriver;fmx;IndySystem;TeeDB;tethering;ITDevCon2012AdapterPackage;inetdbbde;vclib;DBXInterBaseDriver;DataSnapClient;DataSnapServer;DataSnapCommon;DataSnapProviderClient;DBXSybaseASEDriver;DbxCommonDriver;vclimg;dbxcds;DatasnapConnectorsFreePascal;MetropolisUILiveTile;vcldb;vcldsnap;fmxFireDAC;DBXDb2Driver;DBXOracleDriver;CustomIPTransport;vclribbon;dsnap;IndyIPServer;fmxase;vcl;IndyCore;DBXMSSQLDriver;IndyIPCommon;CloudService;FmxTeeUI;FireDACIBDriver;CodeSiteExpressPkg;DataSnapFireDAC;FireDACDBXDriver;soapserver;inetdbxpress;dsnapxml;FireDACInfxDriver;FireDACDb2Driver;adortl;CustomAdaptersMDPackage;FireDACASADriver;bindcompfmx;vcldbx;FireDACODBCDriver;RESTBackendComponents;rtl;dbrtl;DbxClientDriver;FireDACCommon;bindcomp;inetdb;SampleListViewRatingsAppearancePackage;Tee;DBXOdbcDriver;vclFireDAC;xmlrtl;DataSnapNativeClient;svnui;ibxpress;IndyProtocols;DBXMySQLDriver;FireDACCommonDriver;bindcompdbx;bindengine;vclactnband;soaprtl;FMXTee;TeeUI;bindcompvcl;vclie;FireDACADSDriver;vcltouch;VclSmp;FireDACMSSQLDriver;FireDAC;DBXInformixDriver;Intraweb;VCLRESTComponents;DataSnapConnectors;DataSnapServerMidas;dsnapcon;DBXFirebirdDriver;SampleGenerator1Package;inet;fmxobj;FireDACMySQLDriver;soapmidas;vclx;svn;DBXSybaseASADriver;FireDACOracleDriver;fmxdae;RESTComponents;bdertl;FireDACMSAccDriver;dbexpress;DataSnapIndy10ServerTransport;IndyIPClient;$(DCC_UsePackage)</DCC_UsePackage>
|
||||||
@ -258,7 +258,16 @@
|
|||||||
</Excluded_Packages>
|
</Excluded_Packages>
|
||||||
</Delphi.Personality>
|
</Delphi.Personality>
|
||||||
<Deployment Version="3">
|
<Deployment Version="3">
|
||||||
<DeployClass Name="ProjectiOSDeviceResourceRules"/>
|
<DeployClass Name="DependencyModule">
|
||||||
|
<Platform Name="Win32">
|
||||||
|
<Operation>0</Operation>
|
||||||
|
<Extensions>.dll;.bpl</Extensions>
|
||||||
|
</Platform>
|
||||||
|
<Platform Name="OSX32">
|
||||||
|
<Operation>1</Operation>
|
||||||
|
<Extensions>.dylib</Extensions>
|
||||||
|
</Platform>
|
||||||
|
</DeployClass>
|
||||||
<DeployClass Name="ProjectOSXResource">
|
<DeployClass Name="ProjectOSXResource">
|
||||||
<Platform Name="OSX32">
|
<Platform Name="OSX32">
|
||||||
<RemoteDir>Contents\Resources</RemoteDir>
|
<RemoteDir>Contents\Resources</RemoteDir>
|
||||||
@ -598,16 +607,7 @@
|
|||||||
<Operation>1</Operation>
|
<Operation>1</Operation>
|
||||||
</Platform>
|
</Platform>
|
||||||
</DeployClass>
|
</DeployClass>
|
||||||
<DeployClass Name="DependencyModule">
|
<DeployClass Name="ProjectiOSDeviceResourceRules"/>
|
||||||
<Platform Name="Win32">
|
|
||||||
<Operation>0</Operation>
|
|
||||||
<Extensions>.dll;.bpl</Extensions>
|
|
||||||
</Platform>
|
|
||||||
<Platform Name="OSX32">
|
|
||||||
<Operation>1</Operation>
|
|
||||||
<Extensions>.dylib</Extensions>
|
|
||||||
</Platform>
|
|
||||||
</DeployClass>
|
|
||||||
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
|
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
|
||||||
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
|
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
|
||||||
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
|
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
object Form4: TForm4
|
object MainForm: TMainForm
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
Caption = 'Articles CRUD SAMPLE'
|
Caption = 'Articles CRUD SAMPLE'
|
||||||
ClientHeight = 391
|
ClientHeight = 391
|
||||||
ClientWidth = 747
|
ClientWidth = 592
|
||||||
Color = clBtnFace
|
Color = clBtnFace
|
||||||
Font.Charset = DEFAULT_CHARSET
|
Font.Charset = DEFAULT_CHARSET
|
||||||
Font.Color = clWindowText
|
Font.Color = clWindowText
|
||||||
@ -18,56 +18,50 @@ object Form4: TForm4
|
|||||||
object Panel1: TPanel
|
object Panel1: TPanel
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 0
|
Top = 0
|
||||||
Width = 747
|
Width = 592
|
||||||
Height = 43
|
Height = 39
|
||||||
Align = alTop
|
Align = alTop
|
||||||
TabOrder = 0
|
TabOrder = 0
|
||||||
object btnGetListAsynch: TButton
|
|
||||||
AlignWithMargins = True
|
|
||||||
Left = 120
|
|
||||||
Top = 6
|
|
||||||
Width = 89
|
|
||||||
Height = 31
|
|
||||||
Margins.Left = 10
|
|
||||||
Margins.Top = 5
|
|
||||||
Margins.Right = 10
|
|
||||||
Margins.Bottom = 5
|
|
||||||
Align = alLeft
|
|
||||||
Caption = 'Get List Asynch'
|
|
||||||
TabOrder = 0
|
|
||||||
OnClick = btnGetListAsynchClick
|
|
||||||
end
|
|
||||||
object btnGetListSynch: TButton
|
|
||||||
AlignWithMargins = True
|
|
||||||
Left = 11
|
|
||||||
Top = 6
|
|
||||||
Width = 89
|
|
||||||
Height = 31
|
|
||||||
Margins.Left = 10
|
|
||||||
Margins.Top = 5
|
|
||||||
Margins.Right = 10
|
|
||||||
Margins.Bottom = 5
|
|
||||||
Align = alLeft
|
|
||||||
Caption = 'Get List'
|
|
||||||
TabOrder = 1
|
|
||||||
OnClick = Button1Click
|
|
||||||
end
|
|
||||||
object DBNavigator1: TDBNavigator
|
object DBNavigator1: TDBNavigator
|
||||||
Left = 222
|
AlignWithMargins = True
|
||||||
Top = 8
|
Left = 301
|
||||||
Width = 240
|
Top = 4
|
||||||
Height = 25
|
Width = 287
|
||||||
DataSource = DataSource1
|
Height = 31
|
||||||
|
DataSource = dsrcArticles
|
||||||
|
Align = alRight
|
||||||
|
TabOrder = 0
|
||||||
|
end
|
||||||
|
object btnOpen: TButton
|
||||||
|
AlignWithMargins = True
|
||||||
|
Left = 4
|
||||||
|
Top = 4
|
||||||
|
Width = 75
|
||||||
|
Height = 31
|
||||||
|
Align = alLeft
|
||||||
|
Caption = 'Open'
|
||||||
|
TabOrder = 1
|
||||||
|
OnClick = btnOpenClick
|
||||||
|
end
|
||||||
|
object btnClose: TButton
|
||||||
|
AlignWithMargins = True
|
||||||
|
Left = 85
|
||||||
|
Top = 4
|
||||||
|
Width = 75
|
||||||
|
Height = 31
|
||||||
|
Align = alLeft
|
||||||
|
Caption = 'Close'
|
||||||
TabOrder = 2
|
TabOrder = 2
|
||||||
|
OnClick = btnCloseClick
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
object DBGrid1: TDBGrid
|
object DBGrid1: TDBGrid
|
||||||
Left = 0
|
Left = 0
|
||||||
Top = 43
|
Top = 39
|
||||||
Width = 747
|
Width = 592
|
||||||
Height = 348
|
Height = 352
|
||||||
Align = alClient
|
Align = alClient
|
||||||
DataSource = DataSource1
|
DataSource = dsrcArticles
|
||||||
TabOrder = 1
|
TabOrder = 1
|
||||||
TitleFont.Charset = DEFAULT_CHARSET
|
TitleFont.Charset = DEFAULT_CHARSET
|
||||||
TitleFont.Color = clWindowText
|
TitleFont.Color = clWindowText
|
||||||
@ -78,29 +72,37 @@ object Form4: TForm4
|
|||||||
item
|
item
|
||||||
Expanded = False
|
Expanded = False
|
||||||
FieldName = 'id'
|
FieldName = 'id'
|
||||||
|
Title.Caption = '#ID'
|
||||||
Visible = True
|
Visible = True
|
||||||
end
|
end
|
||||||
item
|
item
|
||||||
Expanded = False
|
Expanded = False
|
||||||
FieldName = 'code'
|
FieldName = 'code'
|
||||||
|
Title.Caption = 'Code'
|
||||||
Visible = True
|
Visible = True
|
||||||
end
|
end
|
||||||
item
|
item
|
||||||
Expanded = False
|
Expanded = False
|
||||||
FieldName = 'description'
|
FieldName = 'description'
|
||||||
|
Title.Caption = 'Description'
|
||||||
|
Width = 265
|
||||||
Visible = True
|
Visible = True
|
||||||
end
|
end
|
||||||
item
|
item
|
||||||
Expanded = False
|
Expanded = False
|
||||||
FieldName = 'price'
|
FieldName = 'price'
|
||||||
|
Title.Caption = 'Price'
|
||||||
Visible = True
|
Visible = True
|
||||||
end>
|
end>
|
||||||
end
|
end
|
||||||
object FDMemTable1: TFDMemTable
|
object dsArticles: TFDMemTable
|
||||||
BeforePost = FDMemTable1BeforePost
|
AfterOpen = dsArticlesAfterOpen
|
||||||
BeforeDelete = FDMemTable1BeforeDelete
|
BeforePost = dsArticlesBeforePost
|
||||||
|
BeforeDelete = dsArticlesBeforeDelete
|
||||||
|
BeforeRefresh = dsArticlesBeforeRefresh
|
||||||
FieldDefs = <>
|
FieldDefs = <>
|
||||||
IndexDefs = <>
|
IndexDefs = <>
|
||||||
|
BeforeRowRequest = dsArticlesBeforeRowRequest
|
||||||
FetchOptions.AssignedValues = [evMode]
|
FetchOptions.AssignedValues = [evMode]
|
||||||
FetchOptions.Mode = fmAll
|
FetchOptions.Mode = fmAll
|
||||||
ResourceOptions.AssignedValues = [rvSilentMode]
|
ResourceOptions.AssignedValues = [rvSilentMode]
|
||||||
@ -110,23 +112,23 @@ object Form4: TForm4
|
|||||||
StoreDefs = True
|
StoreDefs = True
|
||||||
Left = 136
|
Left = 136
|
||||||
Top = 120
|
Top = 120
|
||||||
object FDMemTable1id: TIntegerField
|
object dsArticlesid: TIntegerField
|
||||||
FieldName = 'id'
|
FieldName = 'id'
|
||||||
end
|
end
|
||||||
object FDMemTable1code: TStringField
|
object dsArticlescode: TStringField
|
||||||
FieldName = 'code'
|
FieldName = 'code'
|
||||||
end
|
end
|
||||||
object FDMemTable1description: TStringField
|
object dsArticlesdescription: TStringField
|
||||||
FieldName = 'description'
|
FieldName = 'description'
|
||||||
Size = 50
|
Size = 50
|
||||||
end
|
end
|
||||||
object FDMemTable1price: TCurrencyField
|
object dsArticlesprice: TCurrencyField
|
||||||
FieldName = 'price'
|
FieldName = 'price'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
object DataSource1: TDataSource
|
object dsrcArticles: TDataSource
|
||||||
DataSet = FDMemTable1
|
DataSet = dsArticles
|
||||||
Left = 312
|
Left = 136
|
||||||
Top = 192
|
Top = 184
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,137 +11,141 @@ uses
|
|||||||
Vcl.DBCtrls;
|
Vcl.DBCtrls;
|
||||||
|
|
||||||
type
|
type
|
||||||
TForm4 = class(TForm)
|
TMainForm = class(TForm)
|
||||||
Panel1: TPanel;
|
Panel1: TPanel;
|
||||||
DBGrid1: TDBGrid;
|
DBGrid1: TDBGrid;
|
||||||
FDMemTable1: TFDMemTable;
|
dsArticles: TFDMemTable;
|
||||||
FDMemTable1id: TIntegerField;
|
dsArticlesid: TIntegerField;
|
||||||
FDMemTable1code: TStringField;
|
dsArticlescode: TStringField;
|
||||||
FDMemTable1description: TStringField;
|
dsArticlesdescription: TStringField;
|
||||||
FDMemTable1price: TCurrencyField;
|
dsArticlesprice: TCurrencyField;
|
||||||
DataSource1: TDataSource;
|
dsrcArticles: TDataSource;
|
||||||
btnGetListAsynch: TButton;
|
|
||||||
btnGetListSynch: TButton;
|
|
||||||
DBNavigator1: TDBNavigator;
|
DBNavigator1: TDBNavigator;
|
||||||
procedure Button1Click(Sender: TObject);
|
btnOpen: TButton;
|
||||||
procedure btnGetListAsynchClick(Sender: TObject);
|
btnClose: TButton;
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
procedure FDMemTable1BeforePost(DataSet: TDataSet);
|
procedure dsArticlesBeforePost(DataSet: TDataSet);
|
||||||
procedure FDMemTable1BeforeDelete(DataSet: TDataSet);
|
procedure dsArticlesBeforeDelete(DataSet: TDataSet);
|
||||||
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
procedure FormClose(Sender: TObject; var Action: TCloseAction);
|
||||||
|
procedure dsArticlesBeforeRefresh(DataSet: TDataSet);
|
||||||
|
procedure dsArticlesAfterOpen(DataSet: TDataSet);
|
||||||
|
procedure btnOpenClick(Sender: TObject);
|
||||||
|
procedure btnCloseClick(Sender: TObject);
|
||||||
|
procedure dsArticlesBeforeRowRequest(DataSet: TFDDataSet);
|
||||||
private
|
private
|
||||||
CltAsynch: TRESTClient;
|
|
||||||
FLoading: Boolean;
|
FLoading: Boolean;
|
||||||
Clt: TRESTClient;
|
Clt: TRESTClient;
|
||||||
{ Private declarations }
|
{ Private declarations }
|
||||||
procedure ShowError(const AResponse: IRESTResponse);
|
procedure ShowError(const AResponse: IRESTResponse);
|
||||||
public
|
|
||||||
procedure RefreshAsynch;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
Form4: TForm4;
|
MainForm: TMainForm;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
ObjectsMappers;
|
ObjectsMappers, System.UITypes;
|
||||||
|
|
||||||
{$R *.dfm}
|
{$R *.dfm}
|
||||||
|
|
||||||
|
|
||||||
procedure TForm4.btnGetListAsynchClick(Sender: TObject);
|
procedure TMainForm.btnCloseClick(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
// this an asychronous request... just like you could do in jQuery
|
dsArticles.Close;
|
||||||
CltAsynch.Asynch(
|
|
||||||
procedure(Res: IRESTResponse)
|
|
||||||
begin
|
|
||||||
FDMemTable1.Close;
|
|
||||||
FDMemTable1.Open;
|
|
||||||
FLoading := true;
|
|
||||||
FDMemTable1.AppendFromJSONArrayString(Res.BodyAsString);
|
|
||||||
FLoading := false;
|
|
||||||
end,
|
|
||||||
nil, nil, true)
|
|
||||||
.doGET('/articles', []);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm4.Button1Click(Sender: TObject);
|
procedure TMainForm.btnOpenClick(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
RefreshAsynch;
|
dsArticles.Open;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm4.FDMemTable1BeforeDelete(DataSet: TDataSet);
|
procedure TMainForm.dsArticlesAfterOpen(DataSet: TDataSet);
|
||||||
var
|
var
|
||||||
Res: IRESTResponse;
|
Res: IRESTResponse;
|
||||||
begin
|
begin
|
||||||
if FDMemTable1.State = dsBrowse then
|
// this a simple sychronous request...
|
||||||
Res := Clt.DataSetDelete('/articles', FDMemTable1id.AsString);
|
Res := Clt.doGET('/articles', []);
|
||||||
if not(Res.ResponseCode in [200, 201]) then
|
DataSet.DisableControls;
|
||||||
|
try
|
||||||
|
FLoading := true;
|
||||||
|
dsArticles.AppendFromJSONArrayString(Res.BodyAsString);
|
||||||
|
FLoading := false;
|
||||||
|
dsArticles.First;
|
||||||
|
finally
|
||||||
|
DataSet.EnableControls;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.dsArticlesBeforeDelete(DataSet: TDataSet);
|
||||||
|
var
|
||||||
|
Res: IRESTResponse;
|
||||||
|
begin
|
||||||
|
if dsArticles.State = dsBrowse then
|
||||||
|
Res := Clt.DataSetDelete('/articles', dsArticlesid.AsString);
|
||||||
|
if not(Res.ResponseCode in [200]) then
|
||||||
begin
|
begin
|
||||||
ShowError(Res);
|
ShowError(Res);
|
||||||
Abort;
|
Abort;
|
||||||
end
|
end;
|
||||||
else
|
|
||||||
Refresh;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm4.FDMemTable1BeforePost(DataSet: TDataSet);
|
procedure TMainForm.dsArticlesBeforePost(DataSet: TDataSet);
|
||||||
var
|
var
|
||||||
Res: IRESTResponse;
|
Res: IRESTResponse;
|
||||||
begin
|
begin
|
||||||
if not FLoading then
|
if not FLoading then
|
||||||
begin
|
begin
|
||||||
if FDMemTable1.State = dsInsert then
|
if dsArticles.State = dsInsert then
|
||||||
Res := Clt.DataSetInsert('/articles', FDMemTable1)
|
Res := Clt.DataSetInsert('/articles', dsArticles)
|
||||||
else
|
else
|
||||||
Res := Clt.DataSetUpdate('/articles', FDMemTable1, FDMemTable1id.AsString);
|
Res := Clt.DataSetUpdate('/articles', dsArticles, dsArticlesid.AsString);
|
||||||
if not(Res.ResponseCode in [200, 201]) then
|
if not(Res.ResponseCode in [200, 201]) then
|
||||||
begin
|
begin
|
||||||
ShowError(Res);
|
ShowError(Res);
|
||||||
Abort;
|
Abort;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
RefreshAsynch;
|
DataSet.Refresh;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm4.FormClose(Sender: TObject; var Action: TCloseAction);
|
procedure TMainForm.dsArticlesBeforeRefresh(DataSet: TDataSet);
|
||||||
|
//var
|
||||||
|
// Res: IRESTResponse;
|
||||||
|
begin
|
||||||
|
DataSet.Close;
|
||||||
|
DataSet.Open;
|
||||||
|
//
|
||||||
|
// Res := Clt.doGET('/articles', [DataSet.FieldByName('id').AsString]);
|
||||||
|
// FLoading := true;
|
||||||
|
// dsArticles.Edit;
|
||||||
|
// dsArticles.LoadFromJSONObjectString(Res.BodyAsString);
|
||||||
|
// dsArticles.Post;
|
||||||
|
// FLoading := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.dsArticlesBeforeRowRequest(DataSet: TFDDataSet);
|
||||||
|
begin
|
||||||
|
ShowMessage('RowRequest');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
|
||||||
begin
|
begin
|
||||||
CltAsynch.Free;
|
|
||||||
Clt.Free;
|
Clt.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm4.FormCreate(Sender: TObject);
|
procedure TMainForm.FormCreate(Sender: TObject);
|
||||||
begin
|
begin
|
||||||
Clt := TRESTClient.Create('localhost', 8080);
|
Clt := TRESTClient.Create('localhost', 8080);
|
||||||
|
|
||||||
// just for demo, here's the asych version. It is the same :-)
|
|
||||||
// check the GETLISTASYNCH button
|
|
||||||
CltAsynch := TRESTClient.Create('localhost', 8080);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm4.RefreshAsynch;
|
procedure TMainForm.ShowError(const AResponse: IRESTResponse);
|
||||||
var
|
|
||||||
Res: IRESTResponse;
|
|
||||||
lRNo: Integer;
|
|
||||||
begin
|
begin
|
||||||
// this a simple sychronous request...
|
MessageDlg(
|
||||||
Res := Clt.doGET('/articles', []);
|
|
||||||
lRNo := FDMemTable1.RecNo;
|
|
||||||
FDMemTable1.Close;
|
|
||||||
FDMemTable1.Open;
|
|
||||||
FLoading := true;
|
|
||||||
FDMemTable1.AppendFromJSONArrayString(Res.BodyAsString);
|
|
||||||
FDMemTable1.RecNo := lRNo;
|
|
||||||
FLoading := false;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TForm4.ShowError(const AResponse: IRESTResponse);
|
|
||||||
begin
|
|
||||||
ShowMessage(
|
|
||||||
AResponse.ResponseCode.ToString + ': ' + AResponse.ResponseText + sLineBreak +
|
AResponse.ResponseCode.ToString + ': ' + AResponse.ResponseText + sLineBreak +
|
||||||
AResponse.BodyAsJsonObject.Get('message').JsonValue.Value);
|
AResponse.BodyAsJsonObject.Get('message').JsonValue.Value,
|
||||||
|
mtError, [mbOK], 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
<ProjectGuid>{AF04BD45-3137-4757-B1AC-147D4136E52C}</ProjectGuid>
|
<ProjectGuid>{AF04BD45-3137-4757-B1AC-147D4136E52C}</ProjectGuid>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Projects Include="..\articles_crud\articles_crud.dproj">
|
<Projects Include="articles_crud_vcl_client.dproj">
|
||||||
<Dependencies/>
|
<Dependencies/>
|
||||||
</Projects>
|
</Projects>
|
||||||
<Projects Include="articles_crud_vcl_client.dproj">
|
<Projects Include="..\articles_crud_server\articles_crud.dproj">
|
||||||
<Dependencies/>
|
<Dependencies/>
|
||||||
</Projects>
|
</Projects>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -17,15 +17,6 @@
|
|||||||
<Default.Personality/>
|
<Default.Personality/>
|
||||||
</BorlandProject>
|
</BorlandProject>
|
||||||
</ProjectExtensions>
|
</ProjectExtensions>
|
||||||
<Target Name="articles_crud">
|
|
||||||
<MSBuild Projects="..\articles_crud\articles_crud.dproj"/>
|
|
||||||
</Target>
|
|
||||||
<Target Name="articles_crud:Clean">
|
|
||||||
<MSBuild Projects="..\articles_crud\articles_crud.dproj" Targets="Clean"/>
|
|
||||||
</Target>
|
|
||||||
<Target Name="articles_crud:Make">
|
|
||||||
<MSBuild Projects="..\articles_crud\articles_crud.dproj" Targets="Make"/>
|
|
||||||
</Target>
|
|
||||||
<Target Name="articles_crud_vcl_client">
|
<Target Name="articles_crud_vcl_client">
|
||||||
<MSBuild Projects="articles_crud_vcl_client.dproj"/>
|
<MSBuild Projects="articles_crud_vcl_client.dproj"/>
|
||||||
</Target>
|
</Target>
|
||||||
@ -35,14 +26,23 @@
|
|||||||
<Target Name="articles_crud_vcl_client:Make">
|
<Target Name="articles_crud_vcl_client:Make">
|
||||||
<MSBuild Projects="articles_crud_vcl_client.dproj" Targets="Make"/>
|
<MSBuild Projects="articles_crud_vcl_client.dproj" Targets="Make"/>
|
||||||
</Target>
|
</Target>
|
||||||
|
<Target Name="articles_crud">
|
||||||
|
<MSBuild Projects="..\articles_crud_server\articles_crud.dproj"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="articles_crud:Clean">
|
||||||
|
<MSBuild Projects="..\articles_crud_server\articles_crud.dproj" Targets="Clean"/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="articles_crud:Make">
|
||||||
|
<MSBuild Projects="..\articles_crud_server\articles_crud.dproj" Targets="Make"/>
|
||||||
|
</Target>
|
||||||
<Target Name="Build">
|
<Target Name="Build">
|
||||||
<CallTarget Targets="articles_crud;articles_crud_vcl_client"/>
|
<CallTarget Targets="articles_crud_vcl_client;articles_crud"/>
|
||||||
</Target>
|
</Target>
|
||||||
<Target Name="Clean">
|
<Target Name="Clean">
|
||||||
<CallTarget Targets="articles_crud:Clean;articles_crud_vcl_client:Clean"/>
|
<CallTarget Targets="articles_crud_vcl_client:Clean;articles_crud:Clean"/>
|
||||||
</Target>
|
</Target>
|
||||||
<Target Name="Make">
|
<Target Name="Make">
|
||||||
<CallTarget Targets="articles_crud:Make;articles_crud_vcl_client:Make"/>
|
<CallTarget Targets="articles_crud_vcl_client:Make;articles_crud:Make"/>
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="$(BDS)\Bin\CodeGear.Group.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Group.Targets')"/>
|
<Import Project="$(BDS)\Bin\CodeGear.Group.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Group.Targets')"/>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -2,13 +2,13 @@ program articles_crud_vcl_client;
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Vcl.Forms,
|
Vcl.Forms,
|
||||||
MainFormU in 'MainFormU.pas' {Form4};
|
MainFormU in 'MainFormU.pas' {MainForm};
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Application.Initialize;
|
Application.Initialize;
|
||||||
Application.MainFormOnTaskbar := True;
|
Application.MainFormOnTaskbar := True;
|
||||||
Application.CreateForm(TForm4, Form4);
|
Application.CreateForm(TMainForm, MainForm);
|
||||||
Application.Run;
|
Application.Run;
|
||||||
end.
|
end.
|
||||||
|
@ -97,7 +97,7 @@
|
|||||||
<MainSource>MainSource</MainSource>
|
<MainSource>MainSource</MainSource>
|
||||||
</DelphiCompile>
|
</DelphiCompile>
|
||||||
<DCCReference Include="MainFormU.pas">
|
<DCCReference Include="MainFormU.pas">
|
||||||
<Form>Form4</Form>
|
<Form>MainForm</Form>
|
||||||
<FormType>dfm</FormType>
|
<FormType>dfm</FormType>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
<BuildConfiguration Include="Release">
|
<BuildConfiguration Include="Release">
|
||||||
@ -128,27 +128,12 @@
|
|||||||
</Excluded_Packages>
|
</Excluded_Packages>
|
||||||
</Delphi.Personality>
|
</Delphi.Personality>
|
||||||
<Deployment Version="3">
|
<Deployment Version="3">
|
||||||
<DeployClass Name="DependencyModule">
|
<DeployClass Name="ProjectiOSDeviceResourceRules">
|
||||||
<Platform Name="Win32">
|
|
||||||
<Operation>0</Operation>
|
|
||||||
<Extensions>.dll;.bpl</Extensions>
|
|
||||||
</Platform>
|
|
||||||
<Platform Name="iOSDevice64">
|
<Platform Name="iOSDevice64">
|
||||||
<Operation>1</Operation>
|
<Operation>1</Operation>
|
||||||
<Extensions>.dylib</Extensions>
|
|
||||||
</Platform>
|
|
||||||
<Platform Name="OSX32">
|
|
||||||
<RemoteDir>Contents\MacOS</RemoteDir>
|
|
||||||
<Operation>1</Operation>
|
|
||||||
<Extensions>.dylib</Extensions>
|
|
||||||
</Platform>
|
</Platform>
|
||||||
<Platform Name="iOSDevice32">
|
<Platform Name="iOSDevice32">
|
||||||
<Operation>1</Operation>
|
<Operation>1</Operation>
|
||||||
<Extensions>.dylib</Extensions>
|
|
||||||
</Platform>
|
|
||||||
<Platform Name="iOSSimulator">
|
|
||||||
<Operation>1</Operation>
|
|
||||||
<Extensions>.dylib</Extensions>
|
|
||||||
</Platform>
|
</Platform>
|
||||||
</DeployClass>
|
</DeployClass>
|
||||||
<DeployClass Name="ProjectOSXResource">
|
<DeployClass Name="ProjectOSXResource">
|
||||||
@ -528,12 +513,27 @@
|
|||||||
<Operation>1</Operation>
|
<Operation>1</Operation>
|
||||||
</Platform>
|
</Platform>
|
||||||
</DeployClass>
|
</DeployClass>
|
||||||
<DeployClass Name="ProjectiOSDeviceResourceRules">
|
<DeployClass Name="DependencyModule">
|
||||||
|
<Platform Name="Win32">
|
||||||
|
<Operation>0</Operation>
|
||||||
|
<Extensions>.dll;.bpl</Extensions>
|
||||||
|
</Platform>
|
||||||
<Platform Name="iOSDevice64">
|
<Platform Name="iOSDevice64">
|
||||||
<Operation>1</Operation>
|
<Operation>1</Operation>
|
||||||
|
<Extensions>.dylib</Extensions>
|
||||||
|
</Platform>
|
||||||
|
<Platform Name="OSX32">
|
||||||
|
<RemoteDir>Contents\MacOS</RemoteDir>
|
||||||
|
<Operation>1</Operation>
|
||||||
|
<Extensions>.dylib</Extensions>
|
||||||
</Platform>
|
</Platform>
|
||||||
<Platform Name="iOSDevice32">
|
<Platform Name="iOSDevice32">
|
||||||
<Operation>1</Operation>
|
<Operation>1</Operation>
|
||||||
|
<Extensions>.dylib</Extensions>
|
||||||
|
</Platform>
|
||||||
|
<Platform Name="iOSSimulator">
|
||||||
|
<Operation>1</Operation>
|
||||||
|
<Extensions>.dylib</Extensions>
|
||||||
</Platform>
|
</Platform>
|
||||||
</DeployClass>
|
</DeployClass>
|
||||||
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
|
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
|
||||||
|
13
samples/articles_crud_web_angular/.editorconfig
Normal file
13
samples/articles_crud_web_angular/.editorconfig
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
40
samples/articles_crud_web_angular/.gitignore
vendored
Normal file
40
samples/articles_crud_web_angular/.gitignore
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage/*
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# e2e
|
||||||
|
/e2e/*.js
|
||||||
|
/e2e/*.map
|
||||||
|
|
||||||
|
#System Files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
31
samples/articles_crud_web_angular/README.md
Normal file
31
samples/articles_crud_web_angular/README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# RouterSample
|
||||||
|
|
||||||
|
This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.24.
|
||||||
|
|
||||||
|
## Development server
|
||||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Running end-to-end tests
|
||||||
|
|
||||||
|
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||||
|
Before running the tests make sure you are serving the app via `ng serve`.
|
||||||
|
|
||||||
|
## Deploying to Github Pages
|
||||||
|
|
||||||
|
Run `ng github-pages:deploy` to deploy to Github Pages.
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
60
samples/articles_crud_web_angular/angular-cli.json
Normal file
60
samples/articles_crud_web_angular/angular-cli.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0-beta.24",
|
||||||
|
"name": "router-sample"
|
||||||
|
},
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"root": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"assets": [
|
||||||
|
"assets",
|
||||||
|
"favicon.ico"
|
||||||
|
],
|
||||||
|
"index": "index.html",
|
||||||
|
"main": "main.ts",
|
||||||
|
"test": "test.ts",
|
||||||
|
"tsconfig": "tsconfig.json",
|
||||||
|
"prefix": "app",
|
||||||
|
"mobile": false,
|
||||||
|
"styles": [
|
||||||
|
"styles.css",
|
||||||
|
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
|
||||||
|
],
|
||||||
|
"scripts": [],
|
||||||
|
"environments": {
|
||||||
|
"source": "environments/environment.ts",
|
||||||
|
"dev": "environments/environment.ts",
|
||||||
|
"prod": "environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"addons": [],
|
||||||
|
"packages": [],
|
||||||
|
"e2e": {
|
||||||
|
"protractor": {
|
||||||
|
"config": "./protractor.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"karma": {
|
||||||
|
"config": "./karma.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaults": {
|
||||||
|
"styleExt": "css",
|
||||||
|
"prefixInterfaces": false,
|
||||||
|
"inline": {
|
||||||
|
"style": false,
|
||||||
|
"template": false
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"class": false,
|
||||||
|
"component": true,
|
||||||
|
"directive": true,
|
||||||
|
"module": false,
|
||||||
|
"pipe": true,
|
||||||
|
"service": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
samples/articles_crud_web_angular/e2e/app.e2e-spec.ts
Normal file
14
samples/articles_crud_web_angular/e2e/app.e2e-spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { RouterSamplePage } from './app.po';
|
||||||
|
|
||||||
|
describe('router-sample App', function() {
|
||||||
|
let page: RouterSamplePage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new RouterSamplePage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display message saying app works', () => {
|
||||||
|
page.navigateTo();
|
||||||
|
expect(page.getParagraphText()).toEqual('app works!');
|
||||||
|
});
|
||||||
|
});
|
11
samples/articles_crud_web_angular/e2e/app.po.ts
Normal file
11
samples/articles_crud_web_angular/e2e/app.po.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
|
export class RouterSamplePage {
|
||||||
|
navigateTo() {
|
||||||
|
return browser.get('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
getParagraphText() {
|
||||||
|
return element(by.css('app-root h1')).getText();
|
||||||
|
}
|
||||||
|
}
|
16
samples/articles_crud_web_angular/e2e/tsconfig.json
Normal file
16
samples/articles_crud_web_angular/e2e/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": false,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "../dist/out-tsc-e2e",
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es5",
|
||||||
|
"typeRoots": [
|
||||||
|
"../node_modules/@types"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
43
samples/articles_crud_web_angular/karma.conf.js
Normal file
43
samples/articles_crud_web_angular/karma.conf.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Karma configuration file, see link for more information
|
||||||
|
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
basePath: '',
|
||||||
|
frameworks: ['jasmine', 'angular-cli'],
|
||||||
|
plugins: [
|
||||||
|
require('karma-jasmine'),
|
||||||
|
require('karma-chrome-launcher'),
|
||||||
|
require('karma-remap-istanbul'),
|
||||||
|
require('angular-cli/plugins/karma')
|
||||||
|
],
|
||||||
|
files: [
|
||||||
|
{ pattern: './src/test.ts', watched: false }
|
||||||
|
],
|
||||||
|
preprocessors: {
|
||||||
|
'./src/test.ts': ['angular-cli']
|
||||||
|
},
|
||||||
|
mime: {
|
||||||
|
'text/x-typescript': ['ts','tsx']
|
||||||
|
},
|
||||||
|
remapIstanbulReporter: {
|
||||||
|
reports: {
|
||||||
|
html: 'coverage',
|
||||||
|
lcovonly: './coverage/coverage.lcov'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
angularCli: {
|
||||||
|
config: './angular-cli.json',
|
||||||
|
environment: 'dev'
|
||||||
|
},
|
||||||
|
reporters: config.angularCli && config.angularCli.codeCoverage
|
||||||
|
? ['progress', 'karma-remap-istanbul']
|
||||||
|
: ['progress'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false
|
||||||
|
});
|
||||||
|
};
|
49
samples/articles_crud_web_angular/package.json
Normal file
49
samples/articles_crud_web_angular/package.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "router-sample",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"angular-cli": {},
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"lint": "tslint \"src/**/*.ts\"",
|
||||||
|
"test": "ng test",
|
||||||
|
"pree2e": "webdriver-manager update --standalone false --gecko false",
|
||||||
|
"e2e": "protractor"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/common": "^2.3.1",
|
||||||
|
"@angular/compiler": "^2.3.1",
|
||||||
|
"@angular/core": "^2.3.1",
|
||||||
|
"@angular/forms": "^2.3.1",
|
||||||
|
"@angular/http": "^2.3.1",
|
||||||
|
"@angular/platform-browser": "^2.3.1",
|
||||||
|
"@angular/platform-browser-dynamic": "^2.3.1",
|
||||||
|
"@angular/router": "^3.3.1",
|
||||||
|
"bootstrap": "^3.3.7",
|
||||||
|
"core-js": "^2.4.1",
|
||||||
|
"ng2-bootstrap": "^1.2.1",
|
||||||
|
"rxjs": "^5.0.1",
|
||||||
|
"ts-helpers": "^1.1.1",
|
||||||
|
"zone.js": "^0.7.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular/compiler-cli": "^2.3.1",
|
||||||
|
"@types/jasmine": "2.5.38",
|
||||||
|
"@types/node": "^6.0.42",
|
||||||
|
"angular-cli": "1.0.0-beta.24",
|
||||||
|
"codelyzer": "~2.0.0-beta.1",
|
||||||
|
"jasmine-core": "2.5.2",
|
||||||
|
"jasmine-spec-reporter": "2.5.0",
|
||||||
|
"karma": "1.2.0",
|
||||||
|
"karma-chrome-launcher": "^2.0.0",
|
||||||
|
"karma-cli": "^1.0.1",
|
||||||
|
"karma-jasmine": "^1.0.2",
|
||||||
|
"karma-remap-istanbul": "^0.2.1",
|
||||||
|
"protractor": "~4.0.13",
|
||||||
|
"ts-node": "1.2.1",
|
||||||
|
"tslint": "^4.0.2",
|
||||||
|
"typescript": "~2.0.3"
|
||||||
|
}
|
||||||
|
}
|
32
samples/articles_crud_web_angular/protractor.conf.js
Normal file
32
samples/articles_crud_web_angular/protractor.conf.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Protractor configuration file, see link for more information
|
||||||
|
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||||
|
|
||||||
|
/*global jasmine */
|
||||||
|
var SpecReporter = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
|
exports.config = {
|
||||||
|
allScriptsTimeout: 11000,
|
||||||
|
specs: [
|
||||||
|
'./e2e/**/*.e2e-spec.ts'
|
||||||
|
],
|
||||||
|
capabilities: {
|
||||||
|
'browserName': 'chrome'
|
||||||
|
},
|
||||||
|
directConnect: true,
|
||||||
|
baseUrl: 'http://localhost:4200/',
|
||||||
|
framework: 'jasmine',
|
||||||
|
jasmineNodeOpts: {
|
||||||
|
showColors: true,
|
||||||
|
defaultTimeoutInterval: 30000,
|
||||||
|
print: function() {}
|
||||||
|
},
|
||||||
|
useAllAngular2AppRoots: true,
|
||||||
|
beforeLaunch: function() {
|
||||||
|
require('ts-node').register({
|
||||||
|
project: 'e2e'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onPrepare: function() {
|
||||||
|
jasmine.getEnv().addReporter(new SpecReporter());
|
||||||
|
}
|
||||||
|
};
|
22
samples/articles_crud_web_angular/src/app/.vscode/launch.json
vendored
Normal file
22
samples/articles_crud_web_angular/src/app/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Launch Chrome against localhost, with sourcemaps",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:4200",
|
||||||
|
"sourceMaps": true,
|
||||||
|
"webRoot": "${workspaceRoot}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach to Chrome, with sourcemaps",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "attach",
|
||||||
|
"port": 9222,
|
||||||
|
"sourceMaps": true,
|
||||||
|
"webRoot": "${workspaceRoot}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
import { ArticlesListComponent } from './articles-list/articles-list.component';
|
||||||
|
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
|
||||||
|
import { ArticleEditComponent } from './article-edit/article-edit.component';
|
||||||
|
import { HomeComponent } from './home/home.component';
|
||||||
|
import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component';
|
||||||
|
import { ProductListComponent } from './product-list/product-list.component';
|
||||||
|
import { PrivateArea1Component } from './private-area1/private-area1.component';
|
||||||
|
import { PrivateArea2Component } from './private-area2/private-area2.component';
|
||||||
|
import { AuthGuard } from './guards/auth-guard';
|
||||||
|
import { LoginComponent } from './login/login.component';
|
||||||
|
|
||||||
|
// The router selects the route with a first match wins strategy.
|
||||||
|
// Wildcard routes are the least specific routes in the route configuration.
|
||||||
|
// Be sure it is the last route in the configuration.
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: 'home', component: HomeComponent },
|
||||||
|
{ path: 'articles-list', component: ArticlesListComponent },
|
||||||
|
{ path: 'article-edit/:id', component: ArticleEditComponent },
|
||||||
|
// { path: 'shopping', component: ProductListComponent /*data: { title: 'Heroes List'}*/ },
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: '/home',
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'admin', canActivate: [AuthGuard],
|
||||||
|
children: [
|
||||||
|
{ path: 'private1', component: PrivateArea1Component },
|
||||||
|
{ path: 'private2', component: PrivateArea2Component }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: '**', component: PageNotFoundComponent }
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
export class AppRoutingModule { }
|
@ -0,0 +1,3 @@
|
|||||||
|
.active-route {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
32
samples/articles_crud_web_angular/src/app/app.component.html
Normal file
32
samples/articles_crud_web_angular/src/app/app.component.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||||
|
<h1 class="text-center">
|
||||||
|
DelphiMVCFramework
|
||||||
|
<!--<span style="margin-left: 5px; font-size: 60%" class="pull-right"><app-login-logout></app-login-logout></span>-->
|
||||||
|
<!--<span class="pull-right"><app-shopping-cart></app-shopping-cart></span>-->
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<ul class="nav nav-pills nav-stacked">
|
||||||
|
<li role="presentation"><a [routerLink]="['/home']" routerLinkActive="active-route">Home</a></li>
|
||||||
|
<li role="presentation"><a [routerLink]="['/articles-list']" routerLinkActive="active-route">Articles</a></li>
|
||||||
|
<!--<li role="presentation"><a [routerLink]="['/shopping']" routerLinkActive="active-route">Place an Order</a></li>-->
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
<div *ngIf="isLogged">
|
||||||
|
<hr>
|
||||||
|
<ul class="nav nav-pills nav-stacked">
|
||||||
|
<li role="presentation"><a [routerLink]="['/admin','private1']" routerLinkActive="active-route">Private Page1</a></li>
|
||||||
|
<li role="presentation"><a [routerLink]="['/admin','private2']" routerLinkActive="active-route">Private Page2</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-10">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,38 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
|
||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
RouterTestingModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
});
|
||||||
|
TestBed.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(AppComponent);
|
||||||
|
let app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it(`should have as title 'app works!'`, async(() => {
|
||||||
|
let fixture = TestBed.createComponent(AppComponent);
|
||||||
|
let app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('app works!');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should render title in a h1 tag', async(() => {
|
||||||
|
let fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
let compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('h1').textContent).toContain('app works!');
|
||||||
|
}));
|
||||||
|
});
|
17
samples/articles_crud_web_angular/src/app/app.component.ts
Normal file
17
samples/articles_crud_web_angular/src/app/app.component.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { AuthService } from './services/auth.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent implements OnInit {
|
||||||
|
isLogged: boolean;
|
||||||
|
constructor(private authService: AuthService) { }
|
||||||
|
ngOnInit() {
|
||||||
|
this.authService.getSubject().subscribe((username) => {
|
||||||
|
this.isLogged = this.authService.isLogged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
45
samples/articles_crud_web_angular/src/app/app.module.ts
Normal file
45
samples/articles_crud_web_angular/src/app/app.module.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { HttpModule } from '@angular/http';
|
||||||
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
|
||||||
|
import { ArticlesListComponent } from './articles-list/articles-list.component';
|
||||||
|
import { ArticlesService } from './services/articles.service';
|
||||||
|
import { ArticleEditComponent } from './article-edit/article-edit.component';
|
||||||
|
import { HomeComponent } from './home/home.component';
|
||||||
|
import { ProductListComponent } from './product-list/product-list.component';
|
||||||
|
import { ShoppingCartComponent } from './shopping-cart/shopping-cart.component';
|
||||||
|
import { ShoppingCartService } from './services/shopping-cart.service';
|
||||||
|
import { PrivateArea1Component } from './private-area1/private-area1.component';
|
||||||
|
import { PrivateArea2Component } from './private-area2/private-area2.component';
|
||||||
|
import { LoginComponent } from './login/login.component';
|
||||||
|
import { AuthGuard } from './guards/auth-guard';
|
||||||
|
import { AuthService } from './services/auth.service';
|
||||||
|
import { LoginLogoutComponent } from './login-logout/login-logout.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
PageNotFoundComponent,
|
||||||
|
ArticlesListComponent,
|
||||||
|
ArticleEditComponent,
|
||||||
|
HomeComponent,
|
||||||
|
ProductListComponent,
|
||||||
|
ShoppingCartComponent,
|
||||||
|
PrivateArea1Component,
|
||||||
|
PrivateArea2Component,
|
||||||
|
LoginComponent,
|
||||||
|
LoginLogoutComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
FormsModule,
|
||||||
|
HttpModule,
|
||||||
|
AppRoutingModule
|
||||||
|
],
|
||||||
|
providers: [ArticlesService, ShoppingCartService, AuthGuard, AuthService],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
@ -0,0 +1,23 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="codice ">Codice</label>
|
||||||
|
<input [(ngModel)]="article.code" class="form-control" id="codice" name="codice" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="descrizione">Descrizione</label>
|
||||||
|
<input [(ngModel)]="article.description" class="form-control" id="descrizione" name="descrizione" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="prezzo">Prezzo</label>
|
||||||
|
<input [(ngModel)]="article.price" class="form-control" id="prezzo" name="prezzo" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="button" class="btn btn-success" (click)="doSaveArticle()">Save Article</button>
|
||||||
|
<button type="button" class="btn btn-normal" (click)="doCancel()">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ArticleEditComponent } from './article-edit.component';
|
||||||
|
|
||||||
|
describe('ArticleEditComponent', () => {
|
||||||
|
let component: ArticleEditComponent;
|
||||||
|
let fixture: ComponentFixture<ArticleEditComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ArticleEditComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ArticleEditComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,44 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Router, Params } from '@angular/router';
|
||||||
|
import { ArticlesService } from '../services/articles.service';
|
||||||
|
import { Article } from '../models/articolo';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-article-edit',
|
||||||
|
templateUrl: './article-edit.component.html',
|
||||||
|
styleUrls: ['./article-edit.component.css']
|
||||||
|
})
|
||||||
|
export class ArticleEditComponent implements OnInit {
|
||||||
|
private insertMode: boolean;
|
||||||
|
public article: Article = new Article();
|
||||||
|
constructor(private route: ActivatedRoute, private router: Router, private articlesService: ArticlesService) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.params.subscribe((data: Params) => {
|
||||||
|
console.log(data['id']);
|
||||||
|
this.insertMode = +data['id'] === -1;
|
||||||
|
|
||||||
|
if (!this.insertMode) {
|
||||||
|
this.articlesService.getArticolo(+data['id']).subscribe((articolo) => {
|
||||||
|
this.article = articolo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doSaveArticle() {
|
||||||
|
if (this.insertMode) {
|
||||||
|
this.articlesService.addArticolo(this.article)
|
||||||
|
.do(articolo => console.log(articolo))
|
||||||
|
.subscribe((x) => this.router.navigate(['/articles-list']));
|
||||||
|
} else {
|
||||||
|
this.articlesService.saveArticle(this.article)
|
||||||
|
.do(articolo => console.log(articolo))
|
||||||
|
.subscribe((x) => this.router.navigate(['/articles-list']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doCancel() {
|
||||||
|
this.router.navigate(['/articles-list']);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
|
||||||
|
<button type="button" class="pull-right btn btn-large btn-success" (click)="doNewArticle()">New Article</button>
|
||||||
|
|
||||||
|
<table class="table table-condensed animated fadeIn">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Code</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th class="text-right">Price</th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor=" let articolo of articoli, let i=index ">
|
||||||
|
<td>{{articolo.code}}</td>
|
||||||
|
<td>{{articolo.description }}</td>
|
||||||
|
<td class="text-right">{{articolo.price | currency:'EUR':true:'1.2-2'}}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<a href="#" [routerLink]="['/article-edit', articolo.id]" class="btn btn-default"><span class="glyphicon glyphicon-pencil "></span></a>
|
||||||
|
<button class="btn btn-default" (click)="doRemoveArticolo(i)"><span class="glyphicon glyphicon-remove "></span></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- class="row " -->
|
||||||
|
<div class="error " *ngIf="errorMessage ">{{errorMessage}}</div>
|
@ -0,0 +1,41 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Article } from '../models/articolo';
|
||||||
|
import { ArticlesService } from '../services/articles.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-articles-list',
|
||||||
|
templateUrl: 'articles-list.component.html',
|
||||||
|
styles: ['.error {color:red;}'],
|
||||||
|
providers: [ArticlesService]
|
||||||
|
})
|
||||||
|
export class ArticlesListComponent implements OnInit {
|
||||||
|
errorMessage: string;
|
||||||
|
articoli: Article[];
|
||||||
|
|
||||||
|
constructor(private articlesService: ArticlesService, private router: Router) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.getArticoli();
|
||||||
|
}
|
||||||
|
getArticoli() {
|
||||||
|
this.articlesService.getArticoli()
|
||||||
|
.subscribe(
|
||||||
|
articoli => this.articoli = articoli,
|
||||||
|
error => this.errorMessage = <any>error);
|
||||||
|
}
|
||||||
|
|
||||||
|
doNewArticle() {
|
||||||
|
// use the Router service to imperatively navigate to another route
|
||||||
|
this.router.navigate(['/article-edit', -1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
doRemoveArticolo(index) {
|
||||||
|
if (confirm('Are you sure?')) {
|
||||||
|
this.articlesService.removeArticolo(this.articoli[index])
|
||||||
|
.subscribe(resp => {
|
||||||
|
this.getArticoli();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CanActivate, Router } from '@angular/router';
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor(private router: Router, private authService: AuthService) { }
|
||||||
|
|
||||||
|
canActivate() {
|
||||||
|
console.log('AuthGuard#canActivate called');
|
||||||
|
return this.checkLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLogin(): boolean {
|
||||||
|
if (this.authService.isLogged()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate to the login page
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<div class="jumbotron">
|
||||||
|
<h1>DMVCFramework</h1>
|
||||||
|
<p>This demo uses DMVCFramework, typescript and Angular2</p>
|
||||||
|
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
|
||||||
|
</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { HomeComponent } from './home.component';
|
||||||
|
|
||||||
|
describe('HomeComponent', () => {
|
||||||
|
let component: HomeComponent;
|
||||||
|
let fixture: ComponentFixture<HomeComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ HomeComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(HomeComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-home',
|
||||||
|
templateUrl: './home.component.html',
|
||||||
|
styleUrls: ['./home.component.css']
|
||||||
|
})
|
||||||
|
export class HomeComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<button (click)="doLoginLogout()" [ngClass]="{btn: true, 'btn-info': action == 'login', 'btn-danger': action == 'logout'}">{{action}}</button>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { LoginLogoutComponent } from './login-logout.component';
|
||||||
|
|
||||||
|
describe('LoginLogoutComponent', () => {
|
||||||
|
let component: LoginLogoutComponent;
|
||||||
|
let fixture: ComponentFixture<LoginLogoutComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoginLogoutComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoginLogoutComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login-logout',
|
||||||
|
templateUrl: './login-logout.component.html',
|
||||||
|
styleUrls: ['./login-logout.component.css']
|
||||||
|
})
|
||||||
|
export class LoginLogoutComponent implements OnInit {
|
||||||
|
action: string = '';
|
||||||
|
constructor(private authService: AuthService, private router: Router) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.authService.getSubject().subscribe((username) => {
|
||||||
|
if (this.authService.isLogged()) {
|
||||||
|
this.action = 'logout';
|
||||||
|
} else {
|
||||||
|
this.action = 'login';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doLoginLogout() {
|
||||||
|
if (this.authService.isLogged()) {
|
||||||
|
this.authService.logout();
|
||||||
|
this.router.navigate(['/home']);
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Specific styles of signin component
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* General styles
|
||||||
|
*/
|
||||||
|
|
||||||
|
.card-container.card {
|
||||||
|
max-width: 350px;
|
||||||
|
padding: 40px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
font-weight: 700;
|
||||||
|
height: 36px;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Card component
|
||||||
|
*/
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #F7F7F7;
|
||||||
|
/* just in case there no content*/
|
||||||
|
padding: 20px 25px 30px;
|
||||||
|
margin: 0 auto 25px;
|
||||||
|
margin-top: 50px;
|
||||||
|
/* shadows and rounded borders */
|
||||||
|
-moz-border-radius: 2px;
|
||||||
|
-webkit-border-radius: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-img-card {
|
||||||
|
width: 96px;
|
||||||
|
height: 96px;
|
||||||
|
margin: 0 auto 10px;
|
||||||
|
display: block;
|
||||||
|
-moz-border-radius: 50%;
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Form styles
|
||||||
|
*/
|
||||||
|
|
||||||
|
.profile-name-card {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px 0 0;
|
||||||
|
min-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reauth-email {
|
||||||
|
display: block;
|
||||||
|
color: #404040;
|
||||||
|
line-height: 2;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin #inputEmail,
|
||||||
|
.form-signin #inputPassword {
|
||||||
|
direction: ltr;
|
||||||
|
height: 44px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin input[type=email],
|
||||||
|
.form-signin input[type=password],
|
||||||
|
.form-signin input[type=text],
|
||||||
|
.form-signin button {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin .form-control:focus {
|
||||||
|
border-color: rgb(104, 145, 162);
|
||||||
|
outline: 0;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgb(104, 145, 162);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgb(104, 145, 162);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-signin {
|
||||||
|
/*background-color: #4d90fe; */
|
||||||
|
background-color: rgb(104, 145, 162);
|
||||||
|
/* background-color: linear-gradient(rgb(104, 145, 162), rgb(12, 97, 33));*/
|
||||||
|
padding: 0px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 36px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: none;
|
||||||
|
-o-transition: all 0.218s;
|
||||||
|
-moz-transition: all 0.218s;
|
||||||
|
-webkit-transition: all 0.218s;
|
||||||
|
transition: all 0.218s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn.btn-signin:hover,
|
||||||
|
.btn.btn-signin:active,
|
||||||
|
.btn.btn-signin:focus {
|
||||||
|
background-color: rgb(12, 97, 33);
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password {
|
||||||
|
color: rgb(104, 145, 162);
|
||||||
|
}
|
||||||
|
|
||||||
|
.forgot-password:hover,
|
||||||
|
.forgot-password:active,
|
||||||
|
.forgot-password:focus {
|
||||||
|
color: rgb(12, 97, 33);
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="card card-container">
|
||||||
|
<!-- <img class="profile-img-card" src="//lh3.googleusercontent.com/-6V8xOA6M7BA/AAAAAAAAAAI/AAAAAAAAAAA/rzlHcD0KYwo/photo.jpg?sz=120" alt="" /> -->
|
||||||
|
<img id="profile-img" class="profile-img-card" src="//ssl.gstatic.com/accounts/ui/avatar_2x.png" />
|
||||||
|
<p id="profile-name" class="profile-name-card"></p>
|
||||||
|
<form class="form-signin" (ngSubmit)="doLogin()">
|
||||||
|
<span id="reauth-email" class="reauth-email"></span>
|
||||||
|
<input [(ngModel)]="user.username" type="text" name="username" id="inputEmail" class="form-control" placeholder="Username" required autofocus>
|
||||||
|
<input [(ngModel)]="user.password" type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
|
||||||
|
<button class="btn btn-lg btn-primary btn-block btn-signin" type="submit">Sign in</button>
|
||||||
|
<div>{{message}}</div>
|
||||||
|
</form>
|
||||||
|
<!-- /form -->
|
||||||
|
</div>
|
||||||
|
<!-- /card-container -->
|
||||||
|
</div>
|
||||||
|
<!-- /container -->
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
|
describe('LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoginComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,27 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrls: ['./login.component.css']
|
||||||
|
})
|
||||||
|
export class LoginComponent implements OnInit {
|
||||||
|
message: string = '';
|
||||||
|
user: { username: string, password: string } = { username: '', password: '' };
|
||||||
|
constructor(private authService: AuthService, private router: Router) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
doLogin() {
|
||||||
|
this.message = '';
|
||||||
|
if (this.authService.login(this.user.username, this.user.password)) {
|
||||||
|
this.router.navigate(['/admin', 'private1']);
|
||||||
|
} else {
|
||||||
|
this.message = 'Invalid login';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
export class Article {
|
||||||
|
id: number = 0;
|
||||||
|
code: string;
|
||||||
|
description: string;
|
||||||
|
price: number;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<div class="alert alert-danger" role="alert">Page not found</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { PageNotFoundComponent } from './page-not-found.component';
|
||||||
|
|
||||||
|
describe('PageNotFoundComponent', () => {
|
||||||
|
let component: PageNotFoundComponent;
|
||||||
|
let fixture: ComponentFixture<PageNotFoundComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ PageNotFoundComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PageNotFoundComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-not-found',
|
||||||
|
templateUrl: './page-not-found.component.html',
|
||||||
|
styleUrls: ['./page-not-found.component.css']
|
||||||
|
})
|
||||||
|
export class PageNotFoundComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
<p>
|
||||||
|
private-area1 works!
|
||||||
|
</p>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { PrivateArea1Component } from './private-area1.component';
|
||||||
|
|
||||||
|
describe('PrivateArea1Component', () => {
|
||||||
|
let component: PrivateArea1Component;
|
||||||
|
let fixture: ComponentFixture<PrivateArea1Component>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ PrivateArea1Component ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PrivateArea1Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-private-area1',
|
||||||
|
templateUrl: './private-area1.component.html',
|
||||||
|
styleUrls: ['./private-area1.component.css']
|
||||||
|
})
|
||||||
|
export class PrivateArea1Component implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
<p>
|
||||||
|
private-area2 works!
|
||||||
|
</p>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { PrivateArea2Component } from './private-area2.component';
|
||||||
|
|
||||||
|
describe('PrivateArea2Component', () => {
|
||||||
|
let component: PrivateArea2Component;
|
||||||
|
let fixture: ComponentFixture<PrivateArea2Component>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ PrivateArea2Component ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PrivateArea2Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-private-area2',
|
||||||
|
templateUrl: './private-area2.component.html',
|
||||||
|
styleUrls: ['./private-area2.component.css']
|
||||||
|
})
|
||||||
|
export class PrivateArea2Component implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<table class="table table-condensed animated fadeIn">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Code</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th class="text-right">Price</th>
|
||||||
|
<th> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor=" let articolo of articles, let i=index ">
|
||||||
|
<td>{{articolo.code}}</td>
|
||||||
|
<td>{{articolo.description }}</td>
|
||||||
|
<td class="text-right">{{articolo.price | currency:'EUR':true:'1.2-2'}}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<button class="btn btn-default" (click)="doAddProduct(i)"><span class="glyphicon glyphicon-plus"></span></button>
|
||||||
|
<button class="btn btn-default" (click)="doRemoveProduct(i)"><span class="glyphicon glyphicon-minus"></span></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ProductListComponent } from './product-list.component';
|
||||||
|
|
||||||
|
describe('ProductListComponent', () => {
|
||||||
|
let component: ProductListComponent;
|
||||||
|
let fixture: ComponentFixture<ProductListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ProductListComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProductListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,40 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ArticlesService } from '../services/articles.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Article } from '../models/articolo';
|
||||||
|
import { ShoppingCartService } from '../services/shopping-cart.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-product-list',
|
||||||
|
templateUrl: './product-list.component.html',
|
||||||
|
styleUrls: ['./product-list.component.css']
|
||||||
|
})
|
||||||
|
export class ProductListComponent implements OnInit {
|
||||||
|
articles: Array<Article>;
|
||||||
|
constructor(
|
||||||
|
private articlesService: ArticlesService,
|
||||||
|
private router: Router,
|
||||||
|
private cartService: ShoppingCartService) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.getArticoli();
|
||||||
|
}
|
||||||
|
getArticoli() {
|
||||||
|
this.articlesService.getArticoli()
|
||||||
|
.subscribe(
|
||||||
|
articoli => this.articles = articoli);
|
||||||
|
}
|
||||||
|
|
||||||
|
doAddProduct(index: number) {
|
||||||
|
let article: Article = this.articles[index];
|
||||||
|
this.cartService.addProduct(article.id, article.description, article.price);
|
||||||
|
}
|
||||||
|
|
||||||
|
doRemoveProduct(index: number) {
|
||||||
|
let article: Article = this.articles[index];
|
||||||
|
this.cartService.removeProduct(article.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Http, Response, Headers, RequestOptions } from '@angular/http';
|
||||||
|
import { Article } from '../models/articolo';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ArticlesService {
|
||||||
|
private _articoliUrl = 'http://localhost:8080/articles'; // URL to web api
|
||||||
|
|
||||||
|
constructor(private http: Http) { }
|
||||||
|
|
||||||
|
public getArticolo(id: number): Observable<Article> {
|
||||||
|
return this.http
|
||||||
|
.get(this._articoliUrl + '/' + id.toString())
|
||||||
|
.map((res: Response) => <Article>res.json())
|
||||||
|
.catch((err: any) => Observable.throw('Cannot find article'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getArticoli() {
|
||||||
|
return this.http.get(this._articoliUrl)
|
||||||
|
.map(res => <Article[]>res.json())
|
||||||
|
.do(data => console.log(data)) // eyeball results in the console
|
||||||
|
.catch(this.handleError);
|
||||||
|
}
|
||||||
|
private handleError(error: Response) {
|
||||||
|
// in a real world app, we may send the error to some remote logging infrastructure
|
||||||
|
// instead of just logging it to the console
|
||||||
|
console.error(error.text);
|
||||||
|
return Observable.throw(error.text || 'Server error');
|
||||||
|
// return Observable.throw('Server error');
|
||||||
|
}
|
||||||
|
|
||||||
|
public addArticolo(articolo: Article): Observable<Article> {
|
||||||
|
articolo.price = parseFloat(articolo.price.toString());
|
||||||
|
let body = JSON.stringify(articolo);
|
||||||
|
let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||||
|
let options = new RequestOptions({ headers: headers });
|
||||||
|
|
||||||
|
return this.http.post(this._articoliUrl, body, options)
|
||||||
|
.map(res => <Article>res.json())
|
||||||
|
.catch(this.handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
public saveArticle(articolo: Article): Observable<Article> {
|
||||||
|
articolo.price = parseFloat(articolo.price.toString());
|
||||||
|
let body = JSON.stringify(articolo);
|
||||||
|
let headers = new Headers({ 'Content-Type': 'application/json' });
|
||||||
|
let options = new RequestOptions({ headers: headers });
|
||||||
|
|
||||||
|
return this.http.put(
|
||||||
|
this._articoliUrl + '/' + articolo.id.toString(), body, options)
|
||||||
|
/*.map(res => <Articolo>res.json())*/
|
||||||
|
.catch(this.handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeArticolo(articolo: Article) {
|
||||||
|
return this.http.delete(this._articoliUrl + '/' + articolo.id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthService {
|
||||||
|
private userSubject: BehaviorSubject<string>;
|
||||||
|
|
||||||
|
getSubject(): BehaviorSubject<string> {
|
||||||
|
return this.userSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLoggedUser() {
|
||||||
|
return localStorage['username'];
|
||||||
|
}
|
||||||
|
constructor() {
|
||||||
|
let username = '';
|
||||||
|
if (this.isLogged()) {
|
||||||
|
username = this.getLoggedUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userSubject = new BehaviorSubject<string>(username);
|
||||||
|
this.setLoggedUser(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoggedUser(username) {
|
||||||
|
localStorage['username'] = username;
|
||||||
|
this.userSubject.next(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
isLogged() {
|
||||||
|
return !!localStorage['username'];
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.setLoggedUser('');
|
||||||
|
localStorage.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
login(username: string, password: string) {
|
||||||
|
if (username === password) {
|
||||||
|
this.setLoggedUser(username);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||||
|
|
||||||
|
export interface Product {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
quantity: number;
|
||||||
|
price: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ShoppingCartService {
|
||||||
|
private items: Array<Product> = [];
|
||||||
|
private subject: BehaviorSubject<Array<Product>>;
|
||||||
|
|
||||||
|
getSubject(): BehaviorSubject<Array<Product>> {
|
||||||
|
return this.subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.subject = new BehaviorSubject<Array<Product>>([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
addProduct(id: number, name: string, price: number) {
|
||||||
|
let found = false;
|
||||||
|
this.items.map((item: Product, index: number, items: Array<Product>) => {
|
||||||
|
if (+item.id === +id) {
|
||||||
|
item.quantity++;
|
||||||
|
found = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
this.items.push({ id: id, name: name, quantity: 1, price: price });
|
||||||
|
}
|
||||||
|
this.subject.next(this.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeProduct(id: number) {
|
||||||
|
let found = false;
|
||||||
|
this.items.map((item: Product, index: number, items: Array<Product>) => {
|
||||||
|
if (+item.id === +id) {
|
||||||
|
item.quantity--;
|
||||||
|
}
|
||||||
|
if (item.quantity === 0) {
|
||||||
|
items.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// if (!found) {
|
||||||
|
// this.items.push({ id: id, name: name, quantity: 1 });
|
||||||
|
// }
|
||||||
|
this.subject.next(this.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<span class="glyphicon glyphicon-shopping-cart" aria-hidden="true">
|
||||||
|
<span *ngIf="count > 0">
|
||||||
|
{{count}} <span style="font-size: 60%">{{total | currency:"EUR"}}</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ShoppingCartComponent } from './shopping-cart.component';
|
||||||
|
|
||||||
|
describe('ShoppingCartComponent', () => {
|
||||||
|
let component: ShoppingCartComponent;
|
||||||
|
let fixture: ComponentFixture<ShoppingCartComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ShoppingCartComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ShoppingCartComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,31 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ShoppingCartService, Product } from '../services/shopping-cart.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-shopping-cart',
|
||||||
|
templateUrl: './shopping-cart.component.html',
|
||||||
|
styleUrls: ['./shopping-cart.component.css']
|
||||||
|
})
|
||||||
|
export class ShoppingCartComponent implements OnInit {
|
||||||
|
count: number = 0;
|
||||||
|
total: number = 0;
|
||||||
|
constructor(private cartService: ShoppingCartService) {
|
||||||
|
cartService.getSubject().subscribe((value: Array<Product>) => {
|
||||||
|
console.log(value);
|
||||||
|
let c = 0;
|
||||||
|
let total = 0;
|
||||||
|
value.forEach((item: Product, index: number) => {
|
||||||
|
c += item.quantity;
|
||||||
|
console.log(item);
|
||||||
|
total += +item.quantity * +item.price;
|
||||||
|
|
||||||
|
});
|
||||||
|
this.count = c;
|
||||||
|
this.total = total;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: true
|
||||||
|
};
|
@ -0,0 +1,8 @@
|
|||||||
|
// The file contents for the current environment will overwrite these during build.
|
||||||
|
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
|
||||||
|
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
|
||||||
|
// The list of which env maps to which file can be found in `angular-cli.json`.
|
||||||
|
|
||||||
|
export const environment = {
|
||||||
|
production: false
|
||||||
|
};
|
BIN
samples/articles_crud_web_angular/src/favicon.ico
Normal file
BIN
samples/articles_crud_web_angular/src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
14
samples/articles_crud_web_angular/src/index.html
Normal file
14
samples/articles_crud_web_angular/src/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>RouterSample</title>
|
||||||
|
<base href="/">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root>Loading...</app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user