diff --git a/.deployment b/.deployment
deleted file mode 100644
index 0959a5de..00000000
--- a/.deployment
+++ /dev/null
@@ -1,2 +0,0 @@
-[config]
-SCM_SCRIPT_GENERATOR_ARGS=--aspNetCore project.json
\ No newline at end of file
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..04583682
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,12 @@
+.dockerignore
+.env
+.git
+.gitignore
+.vs
+.vscode
+docker-compose.yml
+docker-compose.*.yml
+*/bin
+*/obj
+node_modules
+docs
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..90bafbc1
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,196 @@
+###############################
+# Core EditorConfig Options #
+###############################
+root = true
+
+# All files
+[*]
+indent_style = space
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+insert_final_newline = true
+charset = utf-8-bom
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false
+
+###############################
+# .NET Coding Conventions #
+###############################
+
+# Solution Files
+[*.sln]
+indent_style = tab
+
+# XML Project Files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+
+# Configuration Files
+[*.{json,xml,yml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}]
+indent_size = 2
+
+# Dotnet Code Style Settings
+# See https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
+# See http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers
+[*.{cs,csx,cake,vb}]
+dotnet_sort_system_directives_first = true:warning
+dotnet_style_coalesce_expression = true:warning
+dotnet_style_collection_initializer = true:warning
+dotnet_style_explicit_tuple_names = true:warning
+dotnet_style_null_propagation = true:warning
+dotnet_style_object_initializer = true:warning
+dotnet_style_predefined_type_for_locals_parameters_members = true:warning
+dotnet_style_predefined_type_for_member_access = true:warning
+dotnet_style_qualification_for_event = true:warning
+dotnet_style_qualification_for_field = true:warning
+dotnet_style_qualification_for_method = true:warning
+dotnet_style_qualification_for_property = true:warning
+
+# Naming Symbols
+# constant_fields - Define constant fields
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+# non_private_readonly_fields - Define public, internal and protected readonly fields
+dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, internal, protected
+dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly
+# static_readonly_fields - Define static and readonly fields
+dotnet_naming_symbols.static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.static_readonly_fields.required_modifiers = static, readonly
+# private_readonly_fields - Define private readonly fields
+dotnet_naming_symbols.private_readonly_fields.applicable_accessibilities = private
+dotnet_naming_symbols.private_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.private_readonly_fields.required_modifiers = readonly
+# public_internal_fields - Define public and internal fields
+dotnet_naming_symbols.public_internal_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_internal_fields.applicable_kinds = field
+# private_protected_fields - Define private and protected fields
+dotnet_naming_symbols.private_protected_fields.applicable_accessibilities = private, protected
+dotnet_naming_symbols.private_protected_fields.applicable_kinds = field
+# public_symbols - Define any public symbol
+dotnet_naming_symbols.public_symbols.applicable_accessibilities = public, internal, protected, protected_internal
+dotnet_naming_symbols.public_symbols.applicable_kinds = method, property, event, delegate
+# parameters - Defines any parameter
+dotnet_naming_symbols.parameters.applicable_kinds = parameter
+# non_interface_types - Defines class, struct, enum and delegate types
+dotnet_naming_symbols.non_interface_types.applicable_kinds = class, struct, enum, delegate
+# interface_types - Defines interfaces
+dotnet_naming_symbols.interface_types.applicable_kinds = interface
+
+# Naming Styles
+# camel_case - Define the camelCase style
+dotnet_naming_style.camel_case.capitalization = camel_case
+# pascal_case - Define the Pascal_case style
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+# first_upper - The first character must start with an upper-case character
+dotnet_naming_style.first_upper.capitalization = first_word_upper
+# prefix_interface_interface_with_i - Interfaces must be PascalCase and the first character of an interface must be an 'I'
+dotnet_naming_style.prefix_interface_interface_with_i.capitalization = pascal_case
+dotnet_naming_style.prefix_interface_interface_with_i.required_prefix = I
+
+# Naming Rules
+# Constant fields must be PascalCase
+dotnet_naming_rule.constant_fields_must_be_pascal_case.severity = warning
+dotnet_naming_rule.constant_fields_must_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_must_be_pascal_case.style = pascal_case
+# Public, internal and protected readonly fields must be PascalCase
+dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.severity = warning
+dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.symbols = non_private_readonly_fields
+dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.style = pascal_case
+# Static readonly fields must be PascalCase
+dotnet_naming_rule.static_readonly_fields_must_be_pascal_case.severity = warning
+dotnet_naming_rule.static_readonly_fields_must_be_pascal_case.symbols = static_readonly_fields
+dotnet_naming_rule.static_readonly_fields_must_be_pascal_case.style = pascal_case
+# Private readonly fields must be camelCase
+dotnet_naming_rule.private_readonly_fields_must_be_camel_case.severity = warning
+dotnet_naming_rule.private_readonly_fields_must_be_camel_case.symbols = private_readonly_fields
+dotnet_naming_rule.private_readonly_fields_must_be_camel_case.style = camel_case
+# Public and internal fields must be PascalCase
+dotnet_naming_rule.public_internal_fields_must_be_pascal_case.severity = warning
+dotnet_naming_rule.public_internal_fields_must_be_pascal_case.symbols = public_internal_fields
+dotnet_naming_rule.public_internal_fields_must_be_pascal_case.style = pascal_case
+# Private and protected fields must be camelCase
+dotnet_naming_rule.private_protected_fields_must_be_camel_case.severity = warning
+dotnet_naming_rule.private_protected_fields_must_be_camel_case.symbols = private_protected_fields
+dotnet_naming_rule.private_protected_fields_must_be_camel_case.style = camel_case
+# Public members must be capitalized
+dotnet_naming_rule.public_members_must_be_capitalized.severity = warning
+dotnet_naming_rule.public_members_must_be_capitalized.symbols = public_symbols
+dotnet_naming_rule.public_members_must_be_capitalized.style = first_upper
+# Parameters must be camelCase
+dotnet_naming_rule.parameters_must_be_camel_case.severity = warning
+dotnet_naming_rule.parameters_must_be_camel_case.symbols = parameters
+dotnet_naming_rule.parameters_must_be_camel_case.style = camel_case
+# Class, struct, enum and delegates must be PascalCase
+dotnet_naming_rule.non_interface_types_must_be_pascal_case.severity = warning
+dotnet_naming_rule.non_interface_types_must_be_pascal_case.symbols = non_interface_types
+dotnet_naming_rule.non_interface_types_must_be_pascal_case.style = pascal_case
+# Interfaces must be PascalCase and start with an 'I'
+dotnet_naming_rule.interface_types_must_be_prefixed_with_i.severity = warning
+dotnet_naming_rule.interface_types_must_be_prefixed_with_i.symbols = interface_types
+dotnet_naming_rule.interface_types_must_be_prefixed_with_i.style = prefix_interface_interface_with_i
+
+# C# Code Style Settings
+# See https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
+# See http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers
+[*.{cs,csx,cake}]
+# Indentation Options
+csharp_indent_block_contents = true:warning
+csharp_indent_braces = false:warning
+csharp_indent_case_contents = true:warning
+csharp_indent_labels = no_change:warning
+csharp_indent_switch_labels = true:warning
+# Style Options
+csharp_style_conditional_delegate_call = true:warning
+csharp_style_expression_bodied_accessors = true:warning
+csharp_style_expression_bodied_constructors = true:warning
+csharp_style_expression_bodied_indexers = true:warning
+csharp_style_expression_bodied_methods = true:warning
+csharp_style_expression_bodied_operators = true:warning
+csharp_style_expression_bodied_properties = true:warning
+csharp_style_inlined_variable_declaration = true:warning
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+csharp_style_throw_expression = true:warning
+csharp_style_var_elsewhere = true:warning
+csharp_style_var_for_built_in_types = true:warning
+csharp_style_var_when_type_is_apparent = true:warning
+# New Line Options
+csharp_new_line_before_catch = true:warning
+csharp_new_line_before_else = true:warning
+csharp_new_line_before_finally = true:warning
+csharp_new_line_before_members_in_anonymous_types = true:warning
+csharp_new_line_before_members_in_object_initializers = true:warning
+# BUG: Warning level cannot be set https://github.com/dotnet/roslyn/issues/18010
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true:warning
+# Spacing Options
+csharp_space_after_cast = false:warning
+csharp_space_after_colon_in_inheritance_clause = true:warning
+csharp_space_after_comma = true:warning
+csharp_space_after_dot = false:warning
+csharp_space_after_keywords_in_control_flow_statements = true:warning
+csharp_space_after_semicolon_in_for_statement = true:warning
+csharp_space_around_binary_operators = before_and_after:warning
+csharp_space_around_declaration_statements = do_not_ignore:warning
+csharp_space_before_colon_in_inheritance_clause = true:warning
+csharp_space_before_comma = false:warning
+csharp_space_before_dot = false:warning
+csharp_space_before_semicolon_in_for_statement = false:warning
+csharp_space_before_open_square_brackets = false:warning
+csharp_space_between_empty_square_brackets = false:warning
+csharp_space_between_method_declaration_name_and_open_parenthesis = false:warning
+csharp_space_between_method_declaration_parameter_list_parentheses = false:warning
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false:warning
+csharp_space_between_method_call_name_and_opening_parenthesis = false:warning
+csharp_space_between_method_call_parameter_list_parentheses = false:warning
+csharp_space_between_method_call_empty_parameter_list_parentheses = false:warning
+csharp_space_between_parentheses = expressions:warning
+csharp_space_between_square_brackets = false:warning
+# Wrapping Options
+csharp_preserve_single_line_blocks = true:warning
+csharp_preserve_single_line_statements = false:warning
diff --git a/.env b/.env
new file mode 100644
index 00000000..bb89c961
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+#Use this variable to set your Docker Repository to make it easier to push later.
+#DOCKER_REGISTRY=your repo name
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c2655070..2f41fd03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,12 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
-
+
+# yarn
+yarn.lock
+
+npm-debug.log.*
+
# Build results
[Dd]ebug/
[Dd]ebugPublic/
@@ -28,8 +33,9 @@ Obj/
# Visual Studio 2015 cache/options directory
.vs/
-/wwwroot/dist/**
-/Client/dist/**
+
+/wwwroot/dist/
+/ClientApp/dist/
# MSTest test Results
[Tt]est[Rr]esult*/
@@ -181,6 +187,7 @@ BundleArtifacts/
!*.[Cc]ache/
# Others
+*.db
ClientBin/
~$*
*~
@@ -191,8 +198,10 @@ ClientBin/
orleans.codegen.cs
# Workaround for https://github.com/aspnet/JavaScriptServices/issues/235
-/node_modules/**
-!/node_modules/_placeholder.txt
+/node_modules
+node_modules
+node_modules/**/*
+node_modules/_placeholder.txt
# RIA/Silverlight projects
Generated_Code/
@@ -247,4 +256,10 @@ _Pvt_Extensions
.awcache
# VSCode files
-.vscode/chrome
\ No newline at end of file
+.vscode/chrome
+
+# Jest Code Coverage report
+coverage/
+
+.DS_Store
+package-lock.json
diff --git a/.vscode/launch.json b/.vscode/launch.json
index b37e0d88..c26ab807 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,73 +1,118 @@
{
- "version": "0.2.0",
- "configurations": [
- {
- "name": "[Development] Launch Web",
- "type": "coreclr",
- "request": "launch",
- "preLaunchTask": "build",
- "program": "${workspaceRoot}/bin/Debug/netcoreapp1.1/aspnetcore-angular2-universal.dll",
- "args": [],
- "cwd": "${workspaceRoot}",
- "stopAtEntry": false,
- "internalConsoleOptions": "openOnSessionStart",
- "launchBrowser": {
- "enabled": true,
- "args": "${auto-detect-url}",
- "windows": {
- "command": "cmd.exe",
- "args": "/C start ${auto-detect-url}"
- },
- "osx": {
- "command": "open"
- },
- "linux": {
- "command": "xdg-open"
- }
- },
- "env": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- },
- "sourceFileMap": {
- "/Views": "${workspaceRoot}/Views"
- }
+ "version": "0.2.0",
+ "compounds": [{
+ "name": "[Development] Debug Server & Client",
+ "configurations": ["[Development] Launch Server (no browser)", "[Development] Debug TypeScript"]
+ }],
+ "configurations": [{
+
+ "name": "[Development] Debug TypeScript",
+ "type": "chrome",
+ "request": "launch",
+ "url": "http://localhost:5000/",
+ "webRoot": "${workspaceRoot}/wwwroot",
+ "sourceMapPathOverrides": {
+ "webpack:///./*": "${workspaceRoot}\\*"
+ }
+ },
+ {
+ "name": "[Development] Launch Server (no browser)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceRoot}/bin/Debug/netcoreapp2.1/Asp2017.dll",
+ "args": [],
+ "cwd": "${workspaceRoot}",
+ "stopAtEntry": false,
+ "internalConsoleOptions": "openOnSessionStart",
+ "launchBrowser": {
+ "enabled": false,
+ "args": "${auto-detect-url}",
+ "windows": {
+ "command": "cmd.exe",
+ "args": "/C start ${auto-detect-url}"
},
- {
- "name": "[Production] Launch Web",
- "type": "coreclr",
- "request": "launch",
- "preLaunchTask": "build",
- "program": "${workspaceRoot}/bin/Debug/netcoreapp1.1/aspnetcore-angular2-universal.dll",
- "args": [],
- "cwd": "${workspaceRoot}",
- "stopAtEntry": false,
- "internalConsoleOptions": "openOnSessionStart",
- "launchBrowser": {
- "enabled": true,
- "args": "${auto-detect-url}",
- "windows": {
- "command": "cmd.exe",
- "args": "/C start ${auto-detect-url}"
- },
- "osx": {
- "command": "open"
- },
- "linux": {
- "command": "xdg-open"
- }
- },
- "env": {
- "ASPNETCORE_ENVIRONMENT": "Production"
- },
- "sourceFileMap": {
- "/Views": "${workspaceRoot}/Views"
- }
+ "osx": {
+ "command": "open"
},
- {
- "name": ".NET Core Attach",
- "type": "coreclr",
- "request": "attach",
- "processId": "${command.pickProcess}"
+ "linux": {
+ "command": "xdg-open"
}
- ]
-}
\ No newline at end of file
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceRoot}/Views"
+ }
+ },
+ {
+ "name": "[Development] Launch Web",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceRoot}/bin/Debug/netcoreapp2.1/Asp2017.dll",
+ "args": [],
+ "cwd": "${workspaceRoot}",
+ "stopAtEntry": false,
+ "internalConsoleOptions": "openOnSessionStart",
+ "launchBrowser": {
+ "enabled": true,
+ "args": "${auto-detect-url}",
+ "windows": {
+ "command": "cmd.exe",
+ "args": "/C start ${auto-detect-url}"
+ },
+ "osx": {
+ "command": "open"
+ },
+ "linux": {
+ "command": "xdg-open"
+ }
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceRoot}/Views"
+ }
+ },
+ {
+ "name": "[Production] Launch Web",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "${workspaceRoot}/bin/Debug/netcoreapp2.1/Asp2017.dll",
+ "args": [],
+ "cwd": "${workspaceRoot}",
+ "stopAtEntry": false,
+ "internalConsoleOptions": "openOnSessionStart",
+ "launchBrowser": {
+ "enabled": true,
+ "args": "${auto-detect-url}",
+ "windows": {
+ "command": "cmd.exe",
+ "args": "/C start ${auto-detect-url}"
+ },
+ "osx": {
+ "command": "open"
+ },
+ "linux": {
+ "command": "xdg-open"
+ }
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Production"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceRoot}/src/AspCoreServer/Views"
+ }
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "${command:pickProcess}"
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index c7c1623b..0a455668 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,3 @@
{
- "typescript.tsdk": "./node_modules/typescript/lib"
+ "vsicons.presets.angular": true
}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index adc66239..da1cb069 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -1,4 +1,6 @@
{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
"version": "0.1.0",
"command": "dotnet",
"isShellCommand": true,
@@ -6,10 +8,9 @@
"tasks": [
{
"taskName": "build",
- "args": [
- "${workspaceRoot}/project.json"
- ],
+ "args": ["Asp2017.sln"],
"isBuildCommand": true,
+ "showOutput": "silent",
"problemMatcher": "$msCompile"
}
]
diff --git a/Angular2Spa.sln b/Angular2Spa.sln
deleted file mode 100644
index c9525c1f..00000000
--- a/Angular2Spa.sln
+++ /dev/null
@@ -1,22 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Angular2Spa", "Angular2Spa.xproj", "{8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8F5CB8A9-3086-4B49-A1C2-32A9F89BCA11}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/Angular2Spa.xproj b/Angular2Spa.xproj
deleted file mode 100644
index 14a28869..00000000
--- a/Angular2Spa.xproj
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- 14.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
- true
-
-
-
- 8f5cb8a9-3086-4b49-a1c2-32a9f89bca11
- Angular2Spa
- .\obj
- .\bin\
- v4.5.2
-
-
- 2.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Asp2017.csproj b/Asp2017.csproj
new file mode 100644
index 00000000..b307acba
--- /dev/null
+++ b/Asp2017.csproj
@@ -0,0 +1,67 @@
+
+
+ netcoreapp2.1
+ true
+ Latest
+ false
+ Linux
+ docker-compose.dcproj
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %(DistFiles.Identity)
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Asp2017.sln b/Asp2017.sln
new file mode 100644
index 00000000..9dcc66c0
--- /dev/null
+++ b/Asp2017.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2018
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Asp2017", "Asp2017.csproj", "{BC28E9F7-E6EC-447D-AABD-17683BEAD625}"
+EndProject
+Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{10B71BFC-C3ED-40B0-BB25-E38F04135E17}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BC28E9F7-E6EC-447D-AABD-17683BEAD625}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10B71BFC-C3ED-40B0-BB25-E38F04135E17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10B71BFC-C3ED-40B0-BB25-E38F04135E17}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10B71BFC-C3ED-40B0-BB25-E38F04135E17}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10B71BFC-C3ED-40B0-BB25-E38F04135E17}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DE341460-9041-458F-99D5-43FC7572CFA6}
+ EndGlobalSection
+EndGlobal
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 37edbfe7..00000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,68 +0,0 @@
-# Changelog
-
-> This is a manual changelog at the moment to track the Major changes in the Repo.
-
-## How do I stay up to date when Updates happen in this Starter?
-
-Typically when you want to update your App with the latest from this repo, you should be able to get the latest from here
- and (depending on what has changed), you should be able to just drop in your Client & Server folders on top.
-
-My goal is to point out the more **"Major"** issues & changes below, that might have affected the application in Client/Server aspects.
-For example, some additions made to each platform to add a new Feature (such as Storage that's using dependeny injection).
-Of course if some feature like that isn't important to you, there's no need to copy it over! A lot examples here are to get you up &
-running with a lot of the eco-system of .NET Core & Angular (v2+).
-
-Don't hesitate to create an Issue with anything you run into, and Pull-Requests are always welcome for basic implementations of new features & fixes
-you think others would benefit from as well!
-
-## 1.0.0-rc.0 - 2/9/2017 - RC Release!!
-
-> We're getting closer! I feel the repo is getting much more consistent with all the features we've included in here.
-
-- **Breaking change with file structure**
- - `app.common.module.ts` renamed to `app.module.ts` and moved from `/app/platform-modules/` to `/app/`.
- - Removed `app.routes.ts` and simply placed them within an `[]` within `app.module.ts` to make it easier to add new Components & Routes.
- - [commit here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/7c973efa711a292ecc8f8471c08624bc4ff2102b)
-
-- Deployment updates, now able to Deploy to Azure! [commit here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/bb7e2da5b96040acdfbfa4052547b4a22f0e173c)
-
----
-
-### 1.0.0-beta.6 - 2/1/2017
-
-> Breaking changes to build system
-- Webpack build system has changed to a separate client/server system.
- - Changes to `bootstrap-server.ts`
- - Changes to `Views/Home/Index.cshtml`
- - [Commit here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/5d7c367066bc55b4ab37b82f9335c8be15059fb6)
-
-### 1.0.0-beta.5 - 1/29/2017
-- Feature: Updated to latest Angular 2.4.5
- - This allows us to upgrade Ng2-bootstrap to the latest 1.3.1 version as well (which requires importing the module differently `from 'ng2-bootstrap';` now),
- as well as adding `.forRoot()` when importing it to our own app.common.module.
-- `__2.1.1.workaround.ts` import brought to very **TOP** of `bootstrap-client` & `bootstrap-server` to avoid reflect-metadata issue.
-
-### 1.0.0-beta.4 - 1/27/2017
-- [Closes #48](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/48) Feature: Add CSRF Token cookie so Angular & .NET Core can validate it. - [commit](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/5d0cbe48245889a2b6f1cdfb67bad492d80c85c5)
-- Feature: Sample Entity Framework Core added - [commit](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/b132d6a5707ccdb826b2cfbc2d8610343901b452)
-- Updated Webpack to 2.2.0
-
-### 1.0.0-beta.3 - 1/23/2017
-
-- [Closes #32](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/32) Feature: Added automatic Title update on Route change based on Route `data:{title:''}` -
-[commit](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/b2f15cd16d2dcc43df30b9549d1cf2ced90f66e2)
-
-- [Closes #35](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/35) Implemented Storage service for
-Browser (localStorage) & Server (in memory variable) - [commit](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/a5b3be3cf35c9da4c2bd7b3ede98b07f243cfeac)
-
-- [Closes #34](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/45) Fix karma to handle Angular
-testing - [commit](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/1777f43ca23ede6c46d3cd37c1a2d35605a1355d)
-
-
-### 1.0.0-beta.2 - 1/14/2017
-
-- Update AppComponent to be `` to satisfy TSLint. `bootstrap-server.ts` updated as well.
-
-- Partial revert of ng2-bootstrap dependency down to **1.1.16**, 1.1.20+ require ng 2.4.x (unsupported by Universal atm) [commit](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/524df2df00113d0ee2953b44ae40167112192f89)
-
-- [Closes #33](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/33) Update CSS for bootstrap4 & fix ng2-bootstrap API changes [commit](https://github.com/MarkPieszak/aspnetcore-angular2-universal/commit/d0c0e7d98b9ac043be9880ba2656ddf0f0f2222d)
diff --git a/Client/__2.1.1.workaround.ts b/Client/__2.1.1.workaround.ts
deleted file mode 100644
index c0db5e3a..00000000
--- a/Client/__2.1.1.workaround.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-
-/*
- * THIS IS TEMPORARY TO PATCH 2.1.1+ Core bugs
- */
-
-/* tslint:disable */
-let __compiler__ = require('@angular/compiler');
-import { __platform_browser_private__ } from '@angular/platform-browser';
-import { __core_private__ } from '@angular/core';
-let patch = false;
-if (!__core_private__['ViewUtils']) {
- patch = true;
- __core_private__['ViewUtils'] = __core_private__['view_utils'];
-}
-
-
-
-if (!__compiler__.__compiler_private__) {
- patch = true;
- (__compiler__).__compiler_private__ = {
- SelectorMatcher: __compiler__.SelectorMatcher,
- CssSelector: __compiler__.CssSelector
- }
-}
-
-var __universal__ = require('angular2-platform-node/__private_imports__');
-if (patch) {
- __universal__.ViewUtils = __core_private__['view_utils'];
- __universal__.CssSelector = __compiler__.CssSelector
- __universal__.SelectorMatcher = __compiler__.SelectorMatcher
-}
diff --git a/Client/app/app.component.css b/Client/app/app.component.css
deleted file mode 100644
index 7585968b..00000000
--- a/Client/app/app.component.css
+++ /dev/null
@@ -1,9 +0,0 @@
-body { line-height:18px; }
-ul { padding:10px 25px; }
-ul li { padding:5px 0; }
-
-h1 { border-bottom: 5px #4189C7 solid; }
-h1, h2, h3 { padding:10px 0; }
-
-blockquote { margin:25px 10px; padding:10px 35px 10px 10px; border-left:10px #158a15 solid; background:#edffed ; }
-blockquote a, blockquote a:hover { color:#068006; }
\ No newline at end of file
diff --git a/Client/app/app.component.ts b/Client/app/app.component.ts
deleted file mode 100644
index 0ef26935..00000000
--- a/Client/app/app.component.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Component, ViewEncapsulation, Inject, OnInit, OnDestroy } from '@angular/core';
-import { Router, NavigationEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router';
-import { Title } from '@angular/platform-browser';
-import { Subscription } from 'rxjs/Subscription';
-import { isBrowser } from 'angular2-universal';
-import { Meta, metaStore } from 'app-shared';
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/mergeMap';
-import 'rxjs/add/operator/filter';
-
-@Component({
- selector: 'app-root',
- template: `
-
-
-
-
- `,
- styleUrls: ['./app.component.css'],
- encapsulation: ViewEncapsulation.None
-})
-export class AppComponent implements OnInit, OnDestroy {
-
- private defaultPageTitle: string = 'Angular Universal & ASP.NET Core Starter';
- private sub: Subscription;
-
- constructor(
- public router: Router,
- public activatedRoute: ActivatedRoute,
- public meta: Meta
- ) {}
-
- ngOnInit() {
- // Change "Title" on every navigationEnd event
- // Titles come from the data.title property on all Routes (see app.routes.ts)
- this.changeTitleOnNavigation();
- console.log('oninit');
- }
-
- ngOnDestroy() {
- // Subscription clean-up
- this.sub.unsubscribe();
- }
-
- private changeTitleOnNavigation () {
- this.sub = this.router.events
- .filter(event => event instanceof NavigationEnd)
- .map(() => this.activatedRoute)
- .map(route => {
- while (route.firstChild) route = route.firstChild;
- return route;
- })
- .filter(route => route.outlet === 'primary')
- .mergeMap(route => route.data)
- .subscribe((event) => {
-
- // Set Title if available, otherwise leave the default Title
- const title = event['title']
- ? (event['title'] + ' - ' + this.defaultPageTitle)
- : this.defaultPageTitle;
-
- metaStore.title = title;
- metaStore.meta = {
-
- };
-
- // Temporarily only do this in the Browser
- // Until we can get entire Html doc (this is a .NET issue since we never pass the entire Document (only root-app))
- return isBrowser ? this.meta.setTitle(title) : '';
- });
- }
-
-}
diff --git a/Client/app/app.e2e.ts b/Client/app/app.e2e.ts
deleted file mode 100644
index 8f8f9feb..00000000
--- a/Client/app/app.e2e.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-describe('App', () => {
-
- beforeEach(() => {
- // browser.get('/');
- });
-
- it('e2e test should run', () => {
- let subject = true;
- let result = true;
- expect(subject).toEqual(result);
- });
-
-});
diff --git a/Client/app/app.module.ts b/Client/app/app.module.ts
deleted file mode 100644
index 67bf87ea..00000000
--- a/Client/app/app.module.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * _Common_ NgModule to share between our "BASE" App.Browser & App.Server module platforms
- *
- * If something belongs to BOTH, just put it Here.
- * - If you need something to be very "platform"-specific, put it
- * in the specific one (app.browser or app.server)
- */
-
-import { NgModule } from '@angular/core';
-import { RouterModule, Route } from '@angular/router';
-
-import { Store, StoreModule } from '@ngrx/store';
-import { EffectsModule } from '@ngrx/effects';
-
-import { Ng2BootstrapModule } from 'ng2-bootstrap';
-
-// Main "APP" Root Component
-import { BaseSharedModule, AppComponent, appReducer } from 'app';
-
-// Component imports
-import { NavMenuComponent } from 'app-components';
-
-// Container (aka: "pages") imports
-import {
- HomeComponent,
- RestTestComponent,
- BootstrapComponent,
- LoginComponent,
- ExamplesComponent,
- ChatComponent,
- NotFoundComponent
-} from 'app-containers';
-
-// Provider (aka: "shared" | "services") imports
-import {
- HttpCacheService, CacheService, // Universal : XHR Cache
- ApiGatewayService,
- RxContextDirective,
- Meta
-} from 'app-shared';
-
-//////////////////////////////////////////////////////////////////
-
-// This imports the variable that, in a hot loading situation, holds
-// a reference to the previous application's last state before
-// it was destroyed.
-import { appState } from 'app';
-
-/*
- * All of our Applications ROUTES Go here (imported in MODULES)
- */
-const ROUTES: Route[] = [
- // Base route
- { path: '', redirectTo: 'home', pathMatch: 'full' },
-
- // Other routes
- { path: 'home', component: HomeComponent, data: { title: 'Home'} },
- { path: 'bootstrap', component: BootstrapComponent, data: { title: 'Bootstrap Examples'} },
- { path: 'rest-test', component: RestTestComponent, data: { title: 'WebAPI Examples'} },
- { path: 'login', component: LoginComponent, data: { title: 'Login'} },
- { path: 'examples', component: ExamplesComponent, data: { title: 'Platform Examples'} },
- { path: 'chat', component: ChatComponent, data: { title: 'Chat' } },
- { path: 'not-found', component: NotFoundComponent, data: { title: '404 - Not Found' } },
-
- { // ** LAZY-LOADING EXAMPLE **
- // Notice we don't reference the file anywhere else, imports, declarations, anywhere
- // Webpack2 will provide the separate "chunk" due to System.import
- path: 'faq',
- loadChildren : () => System.import('../containers/+faq/faq.module').then((file: any) => {
- // We use .default here since we use `export default`
- // in the FAQModule NgModule
- return file.default;
- }),
- data: { title: 'FAQ'}
- },
- // loadChildren: '../containers/+faq/faq.module#FAQModule' },
-
- // All else fails - go home
- { path: '**', redirectTo: 'not-found' }
-];
-
-
-const PIPES = [
- // put pipes here
-];
-
-const COMPONENTS = [
- // put shared components here
- AppComponent,
- NavMenuComponent,
- RestTestComponent,
- HomeComponent,
- LoginComponent,
- BootstrapComponent,
- ExamplesComponent,
- ChatComponent,
- NotFoundComponent,
-
- // Directives
- RxContextDirective
-];
-
-const PROVIDERS = [
- // put shared services here
- CacheService,
- HttpCacheService,
- ApiGatewayService,
-
- Meta // MetaService is a cross platform way to change title, and update anything in the
-];
-
-
-/*
- * Common NgModule (remember this gets imported into app.browser.module & app.server.module)
- */
-@NgModule({
- imports: [
- // Do NOT include UniversalModule, HttpModule, or JsonpModule here
-
- // This has ALL the "Common" stuff (CommonModule, FormsModule, ReactiveFormsModule, etc etc)
- // You would import this into your child NgModules so you don't need to duplicate so much code
- BaseSharedModule,
-
- // Angular
- RouterModule,
-
- // NgRx
- StoreModule.provideStore(appReducer, appState),
- EffectsModule,
-
- // Bootstrap
- Ng2BootstrapModule.forRoot(),
-
- // Routing
- RouterModule.forRoot(ROUTES)
- ],
- declarations: [
- ...PIPES,
- ...COMPONENTS
- ],
- providers: [
- ...PROVIDERS
- ]
-})
-export class AppCommonModule {}
diff --git a/Client/app/index.ts b/Client/app/index.ts
deleted file mode 100644
index af9eb30f..00000000
--- a/Client/app/index.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-/*
- This is our "Barrels" index
- Here we can just export all individual things (in this folder)
-
- We're also using TypeScript2's new "paths" to create non-directory import locations
- So instead of having to do something crazy like: "from '../../app/'"
-
- We can just do:
- import { AppState } from 'app';
-
- Makes life easier!
-*/
-
-export * from './app.component';
-
-export * from './state/app.reducer';
-export * from './state/app-state';
-export * from './state/hmr';
-
-export * from './shared-module/base.shared.module';
diff --git a/Client/app/platform-modules/app.browser.module.ts b/Client/app/platform-modules/app.browser.module.ts
deleted file mode 100644
index ee3799f1..00000000
--- a/Client/app/platform-modules/app.browser.module.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { NgModule } from '@angular/core';
-import { RouterModule } from '@angular/router';
-import { StoreDevtoolsModule } from '@ngrx/store-devtools';
-import { Store, StoreModule } from '@ngrx/store';
-// for AoT we need to manually split universal packages (/browser & /node)
-import { UniversalModule, isBrowser, isNode, AUTO_PREBOOT } from 'angular2-universal/browser';
-
-import { AppCommonModule } from '../app.module';
-import { AppComponent } from 'app';
-// Universal : XHR Cache
-import { CacheService, StorageService, BrowserStorage } from 'app-shared';
-
-export const UNIVERSAL_KEY = 'UNIVERSAL_CACHE';
-
-export function getRequest() {
- return {};
-}
-export function getResponse() {
- return {};
-}
-
-@NgModule({
- bootstrap: [ AppComponent ],
-
- imports: [
- // "UniversalModule" Must be first import.
- // ** NOTE ** : This automatically imports BrowserModule, HttpModule, and JsonpModule for Browser,
- // and NodeModule, NodeHttpModule etc for the server.
- UniversalModule,
-
- AppCommonModule,
-
- // NgRx
- StoreDevtoolsModule.instrumentOnlyWithExtension()
- ],
- providers: [
- // Angular -Universal- providers below ::
- // Use them as found in the example in /containers/home.component using for example:
- // ` @Inject('isBrowser') private isBrowser: boolean ` in your constructor
- { provide: 'isBrowser', useValue: isBrowser },
- { provide: 'isNode', useValue: isNode },
-
- { provide: 'req', useFactory: getRequest },
- { provide: 'res', useFactory: getResponse },
-
- // We're using Dependency Injection here to use a Browser specific "Storage" (localStorage here) through the empty shell class StorageService
- // The server will use a different one, since window & localStorage do not exist there
- { provide: StorageService, useClass: BrowserStorage }
-
- // Universal concept. Uncomment this if you want to Turn OFF auto preboot complete
- // { provide: AUTO_PREBOOT, useValue: false }
-
- // Other providers you want to add that you don't want shared in "Common" but are browser only
- ]
-})
-
-export class AppBrowserModule {
-
- constructor(public cache: CacheService) {
- this.doRehydrate();
- }
-
- // Universal Cache "hook"
- doRehydrate() {
- let defaultValue = {};
- let serverCache = this._getCacheValue(CacheService.KEY, defaultValue);
- this.cache.rehydrate(serverCache);
- }
-
- // Universal Cache "hook
- _getCacheValue(key: string, defaultValue: any): any {
- // Get cache that came from the server
- const win: any = window;
- if (win[UNIVERSAL_KEY] && win[UNIVERSAL_KEY][key]) {
- let serverCache = defaultValue;
- try {
- serverCache = JSON.parse(win[UNIVERSAL_KEY][key]);
- if (typeof serverCache !== typeof defaultValue) {
- console.log('Angular Universal: The type of data from the server is different from the default value type');
- serverCache = defaultValue;
- }
- } catch (e) {
- console.log('Angular Universal: There was a problem parsing the server data during rehydrate');
- serverCache = defaultValue;
- }
- return serverCache;
- } else {
- console.log('Angular Universal: UNIVERSAL_CACHE is missing');
- }
- return defaultValue;
- }
-}
diff --git a/Client/app/platform-modules/app.server.module.ts b/Client/app/platform-modules/app.server.module.ts
deleted file mode 100644
index a23fb2c5..00000000
--- a/Client/app/platform-modules/app.server.module.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-
-import { NgModule } from '@angular/core';
-import { RouterModule } from '@angular/router';
-import { StoreDevtoolsModule } from '@ngrx/store-devtools';
-import { Store, StoreModule } from '@ngrx/store';
-// for AoT we need to manually split universal packages (/browser & /node)
-import { UniversalModule, isBrowser, isNode } from 'angular2-universal/node';
-
-import { AppCommonModule } from '../app.module';
-import { AppComponent } from 'app';
-// Universal : XHR Cache
-import { CacheService, StorageService, ServerStorage } from 'app-shared';
-
-export function getRequest() {
- return Zone.current.get('req') || {};
-}
-export function getResponse() {
- return Zone.current.get('res') || {};
-}
-
-@NgModule({
- bootstrap: [ AppComponent ],
- imports: [
- // "UniversalModule" Must be first import.
- // ** NOTE ** : This automatically imports BrowserModule, HttpModule, and JsonpModule for Browser,
- // and NodeModule, NodeHttpModule etc for the server.
- UniversalModule,
-
- AppCommonModule
- ],
- providers: [
- // Angular -Universal- providers below ::
- // Use them as found in the example in /containers/home.component using for example:
- // ` @Inject('isBrowser') private isBrowser: boolean ` in your constructor
- { provide: 'isBrowser', useValue: isBrowser },
- { provide: 'isNode', useValue: isNode },
-
- { provide: 'req', useFactory: getRequest },
- { provide: 'res', useFactory: getResponse },
-
- // We're using Dependency Injection here to use a Server/Node specific "Storage" through the empty shell class StorageService
- { provide: StorageService, useClass: ServerStorage }
-
-
- // Other providers you want to add that you don't want shared in "Common" but are browser only
- ]
-})
-
-export class AppServerModule {
-
- constructor(public cache: CacheService) { }
-
- /** Universal Cache "hook"
- * We need to use the arrow function here to bind the context as this is a gotcha
- * in Universal for now until it's fixed
- */
- universalDoDehydrate = (universalCache) => {
- console.log('universalDoDehydrate ****');
- universalCache[CacheService.KEY] = JSON.stringify(this.cache.dehydrate());
- }
-
- /** Universal Cache "hook"
- * Clear the cache after it's rendered
- */
- universalAfterDehydrate = () => {
- this.cache.clear();
- }
-}
diff --git a/Client/app/shared-module/base.shared.module.ts b/Client/app/shared-module/base.shared.module.ts
deleted file mode 100644
index d1cc7f7b..00000000
--- a/Client/app/shared-module/base.shared.module.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * --Base-- SHARED Module
- *
- * This has the most "basic" Shared imports that can be imported into
- * all children (lazy-loaded for example) NgModules.
- * (ie: Admin NgModule can import this, to import all the basic App functionality, FormsModule, CommonModule etc)
- */
-
-import { NgModule } from '@angular/core';
-import { CommonModule } from '@angular/common';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-
-@NgModule({
- imports: [
- CommonModule
- ],
- exports: [
- CommonModule, FormsModule, ReactiveFormsModule
- ]
-})
-export class BaseSharedModule {
-
-}
-
-
diff --git a/Client/app/state/app-state.ts b/Client/app/state/app-state.ts
deleted file mode 100644
index 2bf2dd60..00000000
--- a/Client/app/state/app-state.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Model definition for the (immutable) application state.
- */
-import { TypedRecord, makeTypedFactory } from 'typed-immutable-record';
-import { List } from 'immutable';
-
-// The TypeScript interface that defines the application state's properties.
-// This is to be imported wherever a reference to the app state is used
-// (reducers, components, services...)
-export interface AppState {
- loggedIn: boolean;
- loggedInUser: {};
-}
-
-// An Immutable.js Record implementation of the AppState interface.
-// This only needs to be imported by reducers, since they produce new versions
-// of the state. Components should only ever read the state, never change it,
-// so they should only need the interface, not the record.
-export interface AppStateRecord extends TypedRecord, AppState { }
-
-// An Immutable.js record factory for the record.
-export const appStateFactory = makeTypedFactory({
- loggedIn : false,
- loggedInUser: {}
-});
diff --git a/Client/app/state/app.reducer.ts b/Client/app/state/app.reducer.ts
deleted file mode 100644
index dd59f834..00000000
--- a/Client/app/state/app.reducer.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * The main (and only) @ngrx/store reducer for the application.
- *
- * This implements the application's core logic by handling actions
- * and producing new versions of the immutable AppState record
- * based on those actions.
- */
-import { ActionReducer, Action } from '@ngrx/store';
-import { List, Range } from 'immutable';
-import { AppStateRecord, appStateFactory } from 'app';
-
-// Action definitions
-export const LOGIN_USER = 'LOGIN_USER';
-export const LOGOUT_USER = 'LOGOUT_USER';
-
-// The reducer function. Receives actions and produces new application states.
-export const appReducer: ActionReducer = (state = makeInitialState(), action: Action) => {
-
- switch (action.type) {
-
- case LOGIN_USER:
- return state.merge({ loggedInUser: action.payload, loggedIn: true });
-
- case LOGOUT_USER:
- return state.merge(makeInitialState());
-
- default:
- return state;
- }
-};
-
-// Initial AppState, used to bootstrap the reducer.
-function makeInitialState() {
- return appStateFactory({
- loggedIn: false,
- loggedInUser: {}
- });
-}
-
diff --git a/Client/app/state/hmr.ts b/Client/app/state/hmr.ts
deleted file mode 100644
index 32dde379..00000000
--- a/Client/app/state/hmr.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { ApplicationRef, NgModuleRef } from '@angular/core';
-import { Store } from '@ngrx/store';
-import { createNewHosts, removeNgStyles } from '@angularclass/hmr';
-
-import 'rxjs/add/operator/take';
-
-
-// This variable will contain the application state after a module has been
-// unloaded. From here it is read to the next application version when it's
-// loaded (see app/app.module.ts).
-export let appState: any;
-
-// Called from main.ts when a hot bootstrap should be done.
-// This function is called every time the application loads
-// (first when the page loads, and then again after each hot reload)
-export function handleHmr(
- module: any, // The module that we're handling HMR for (it'll be the main.ts module)
- bootstrap: () => Promise>) { // The bootstrap function (comes from main.ts)
-
- // Store a reference to the NgModule that we will bootstrap.
- // We'll need it during unload.
- let moduleRef: NgModuleRef;
-
- // Bootstrap the module and grab the NgModule reference from the
- // promise when it's resolved. This will start the application.
- bootstrap()
- .then(mod => moduleRef = mod);
-
- // Let Webpack know that we can handle hot loading for this module
- module.hot.accept();
-
- // Attach a callback to module unload. This'll be called during a hot
- // reload, before the new version of the application comes in. We need to:
- // 1) Grab the current state of the previous application so we can reuse it.
- // 2) Destroy the previous application so that the new one can load cleanly.
- module.hot.dispose(() => {
- // Grab a reference to the running Angular application.
- const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
- // Grab a reference to the application's @ngrx/store.
- const store: Store = moduleRef.injector.get(Store);
- // Get the current state from the Store. The store is an Observable so
- // we can use the Observable API to get the state. We'll get it synchronously
- // though this code may look like we might not.
- store.take(1).subscribe(s => {
- appState = s;
- });
-
- // When an Angular app is destroyed, it will also remove the DOM elements
- // of its root component(s) from the page. When doing hot loading, this is
- // a problem because the next version of the app will have nothing to
- // attach to. We need to clone the DOM nodes of the current application's root
- // component(s)
- const cmpLocations = appRef.components.map(cmp => cmp.location.nativeElement);
- const disposeOldHosts = createNewHosts(cmpLocations);
- moduleRef.destroy();
- removeNgStyles();
- disposeOldHosts();
-
- // After this, the next version of the app will load momentarily.
- // Webpack dev server will execute the new `main.ts` which will then call
- // `handleHmr` again...
- });
-
-}
diff --git a/Client/bootstrap-client.ts b/Client/bootstrap-client.ts
deleted file mode 100644
index 72eae9b4..00000000
--- a/Client/bootstrap-client.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import 'angular2-universal-polyfills/browser'; // This needs to be at the top, Universal neccessary polyfills
-import './__2.1.1.workaround.ts'; // temporary until 2.1.1 things are patched in Core
-
-import { enableProdMode } from '@angular/core';
-// We're going to let Universal take over the Clients "bootstrap" (instead of the normal platformBrowserDynamic)
-import { platformUniversalDynamic } from 'angular2-universal';
-import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
-
-// HMR state management
-import { handleHmr } from 'app';
-// Grab the browser-specific NgModule
-import { AppBrowserModule } from './app/platform-modules/app.browser.module';
-
-let platform;
-
-if (process.env.production) {
- enableProdMode();
- platform = platformUniversalDynamic();
-} else {
- // Development mode
- platform = platformBrowserDynamic();
-}
-
-// Boot the application normally
-const bootApplication = () => platform.bootstrapModule(AppBrowserModule);
-
-// HMR bootstrap overload
-const hmrBootstrap = () => { handleHmr(module, bootApplication); };
-
-if ((module).hot && process.env.development) {
- hmrBootstrap();
-} else {
- bootApplication();
-}
-
diff --git a/Client/bootstrap-server.ts b/Client/bootstrap-server.ts
deleted file mode 100644
index 0a625805..00000000
--- a/Client/bootstrap-server.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import 'angular2-universal-polyfills/node';
-import './__2.1.1.workaround.ts';
-import 'zone.js';
-
-import { enableProdMode } from '@angular/core';
-import { platformNodeDynamic } from 'angular2-universal';
-
-import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
-
-// Grab the (Node) server-specific NgModule
-import { AppServerModule } from './app/platform-modules/app.server.module';
-import { metaStore } from 'app-shared';
-
-enableProdMode();
-
-const platform = platformNodeDynamic();
-
-export default createServerRenderer(params => {
-
- // Our Root application document
- const doc = '';
-
- return new Promise((resolve, reject) => {
- const requestZone = Zone.current.fork({
- name: 'Angular-Universal Request',
- properties: {
- ngModule: AppServerModule,
- baseUrl: '/',
- requestUrl: params.url,
- originUrl: params.origin,
- preboot: false,
- document: doc
- },
- onHandleError: (parentZone, currentZone, targetZone, error) => {
- // If any error occurs while rendering the module, reject the whole operation
- reject(error);
- return true;
- }
- });
-
- return requestZone.run>(() => platform.serializeModule(AppServerModule)).then(html => {
- resolve({ html: html, globals: metaStore });
- }, reject);
- });
-});
-
-
-
-
-// export default function (params: IParams): Promise<{ html: string, globals?: any }> {
-
-// // Our Root application document
-// const doc = '';
-
-// // hold platform reference
-// const platformRef = platformNodeDynamic();
-
-// let platformConfig = {
-// ngModule: AppServerModule,
-// document: doc,
-// preboot: false,
-// baseUrl: '/',
-// requestUrl: params.url,
-// originUrl: params.origin
-// };
-
-// // defaults
-// let cancel = false;
-
-// const _config = Object.assign({
-// get cancel() { return cancel; },
-// cancelHandler() { return Zone.current.get('cancel'); }
-// }, platformConfig);
-
-// // for each user
-// const zone = Zone.current.fork({
-// name: 'UNIVERSAL request',
-// properties: _config
-// });
-
-
-// return Promise.resolve(
-// zone.run(() => {
-// return platformRef.serializeModule(Zone.current.get('ngModule'));
-// })
-// ).then(html => {
-
-// // Something went wrong, return the original blank document at least
-// if (typeof html !== 'string') {
-// return { html: doc };
-// }
-
-// /* Successfully serialized Application
-// * You can pass "Globals" here if you want to pass some data to every request
-// * (also you could pass in params.data if you want to pass data from the server that came through the Views/Index.cshtml page
-// * inside of the asp-prerender-data="" attribute
-// * globals: params.data
-// */
-// return { html, globals: metaStore };
-
-// }).catch(err => {
-
-// console.log(err);
-// return { html: doc };
-
-// });
-
-// }
-
-// export interface IParams {
-// origin: string;
-// url: string;
-// absoluteUrl: string;
-// data: {}; // custom user data sent from server (through asp-prerender-data="" attribute on index.cshtml)
-// }
diff --git a/Client/components/index.ts b/Client/components/index.ts
deleted file mode 100644
index 6131fc57..00000000
--- a/Client/components/index.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-
-/*
- This is our "Barrels" index
- Here we can just export all individual things (in this folder)
-
- We're also using TypeScript2's new "paths" to create non-directory import locations
- So instead of having to do something crazy like: "from '../../components/'"
-
- We can just do:
- import { NavMenuComponent } from 'app-components';
-
- Makes life easier!
-*/
-
-export * from './navmenu/navmenu.component';
diff --git a/Client/components/navmenu/navmenu.component.css b/Client/components/navmenu/navmenu.component.css
deleted file mode 100644
index 558ad588..00000000
--- a/Client/components/navmenu/navmenu.component.css
+++ /dev/null
@@ -1,56 +0,0 @@
-li .fa {
- margin-right: 10px;
-}
-
-/* Highlighting rules for nav menu items */
-li.link-active a,
-li.link-active a:hover,
-li.link-active a:focus {
- background-color: #4189C7;
- color: white;
-}
-
-/* Keep the nav menu independent of scrolling and on top of other items */
-.nav { }
-.nav li { display:inline-block; }
-.nav li a { padding:10px 20px; }
-.nav li a:hover, .nav li a:focus { text-decoration: none; }
-
-@media (min-width: 768px) {
- /* On small screens, convert the nav menu to a vertical sidebar */
- .main-nav {
- height: 100%;
- width: calc(25% - 20px);
- }
- .navbar {
- border-radius: 0px;
- border-width: 0px;
- height: 100%;
- }
- .navbar-header {
- float: none;
- }
- .navbar-collapse {
- border-top: 1px solid #444;
- padding: 0px;
- }
- .navbar ul {
- float: none;
- }
- .navbar li {
- float: none;
- font-size: 15px;
- margin: 6px;
- }
- .navbar li a {
- padding: 10px 16px;
- border-radius: 4px;
- }
- .navbar a {
- /* If a menu item's text is too long, truncate it */
- width: 100%;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-}
diff --git a/Client/components/navmenu/navmenu.component.html b/Client/components/navmenu/navmenu.component.html
deleted file mode 100644
index 13540a79..00000000
--- a/Client/components/navmenu/navmenu.component.html
+++ /dev/null
@@ -1,47 +0,0 @@
-
\ No newline at end of file
diff --git a/Client/components/navmenu/navmenu.component.ts b/Client/components/navmenu/navmenu.component.ts
deleted file mode 100644
index 280dd109..00000000
--- a/Client/components/navmenu/navmenu.component.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { Store } from '@ngrx/store';
-import { Observable } from 'rxjs/Observable';
-
-import { AppState, LOGOUT_USER } from 'app';
-
-@Component({
- selector: 'app-nav-menu',
- templateUrl: './navmenu.component.html',
- styleUrls: ['./navmenu.component.css']
-})
-export class NavMenuComponent implements OnInit {
-
- loggedIn$: {};
- user$: {};
-
- // Use "constructor"s only for dependency injection
- constructor(private store: Store) {}
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit () {
- this.store.select('loggedIn').subscribe(loggedIn => {
- this.loggedIn$ = loggedIn;
- });
-
- this.store.select('loggedInUser').subscribe(user => {
- this.user$ = user;
- });
- }
-
- logout() {
- this.store.dispatch({ type: LOGOUT_USER });
- }
-}
diff --git a/Client/containers/+faq/faq.component.html b/Client/containers/+faq/faq.component.html
deleted file mode 100644
index 1254350a..00000000
--- a/Client/containers/+faq/faq.component.html
+++ /dev/null
@@ -1,69 +0,0 @@
-
FAQ
-
-
- Fun fact: This Component was lazy loaded :)
- Check your Network tab to see that the JS file came only when you first hit this Route.
-
-
-
How to add a route in the app:
-
-
-
Create a folder & your Component in Client\Containers\
-
Add the Component to: Client\Containers\index.ts (this is the "barrels" file for all containers)
-
Add the Component to MODULES & ROUTES within: \Client\app\platform-modules\app.module.ts
-
-
-
How can I disable Universal / SSR (Server-side rendering)?
-
-To disable SSR, go to Views/Home/Index.cshtml and remove asp-prerender-module="Client/bootstrap-server" from the "app-root"
-component there in the cshtml file.
-
-
How do I prevent XHR calls from running again on the Client?
-
-Using the provided GET from HttpCacheService as opposed to regular Http, it will automatically Cache the
-response on the server, and pass it down through the html for you, and when the Client tries to run it again,
-it will instantly grab the result from there.
-
-This is essential for important XHR calls on a page that happen on page load
-
-
How do I have code run only in the Browser?
-
-Angular Universal has isBrowser & isNode you can import from angular2-universal to conditionally run code.
-This is perfect for situations where code could error on the server. Also, always remember that things like
-setTimeout / setInterval / etc should always be wrapped in this, as you want to completely avoid doing them on the Server.
-
-
-{{ "
-import { isBrowser } from 'angular2-universal';
-
-if (isBrowser) {
- // do something only in the Browser
-}
-"}}
-
-
-
How can I use Material2 with this Repo?
-
-For now, Material2 is still in beta, and isn't fully functioning with Universal (it will sometime soon though!),
-so temporarily disable SSR (server-side rendering) so you can use it within your application until updates come
-from Material, and you can have it rendered on the server as well. Read the Material docs to see how to add Material
-to your Angular application, with SSR disabled everything should work without any problems.
-
-
How can I use jQuery and/or some jQuery plugins with Angular Universal?
-
-
Note: If at all possible, try to avoid using jQuery or libraries dependent on it, as there are better,
- more abstract ways of dealing with the DOM in Angular (2+) such as using the Renderer, etc.
-
-Yes, of course but there are a few things you need to setup before doing this. First, make sure jQuery is included in
-webpack vendor file, and that you have a webpack Plugin setup for it. {{ "new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' })" }}
-
-Now, make sure any "plugins" etc that you have, are only included in your bootstrap-client.ts file.
-(ie: import 'slick-carousel';) In a Component you want to use jQuery, make sure to import it near the top like so:
-
-
-import * as $ from 'jquery';
-
-
-Always make sure to wrap anything jQuery oriented in Universal's isBrowser conditional!
-
-
\ No newline at end of file
diff --git a/Client/containers/+faq/faq.component.ts b/Client/containers/+faq/faq.component.ts
deleted file mode 100644
index 01328b46..00000000
--- a/Client/containers/+faq/faq.component.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Component, Inject, ChangeDetectionStrategy, ViewEncapsulation, OnInit } from '@angular/core';
-
-@Component({
- changeDetection: ChangeDetectionStrategy.Default,
- encapsulation: ViewEncapsulation.Emulated,
- selector: 'app-faq',
- templateUrl: './faq.component.html'
-})
-export class FAQComponent implements OnInit {
-
- // Use "constructor"s only for dependency injection
- constructor() { }
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit() {
- console.log('\n\nFAQ Component lazy-loaded!!\n\n');
- }
-
-}
diff --git a/Client/containers/+faq/faq.module.ts b/Client/containers/+faq/faq.module.ts
deleted file mode 100644
index 369c5f34..00000000
--- a/Client/containers/+faq/faq.module.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Lazy-Loaded Module & Component
- * You can see that it wasn't referenced anywhere in the app / modules
- * Except for in the app.routes.ts file
- */
-
-import { NgModule } from '@angular/core';
-import { RouterModule } from '@angular/router';
-
-import { BaseSharedModule } from 'app';
-import { FAQComponent } from './faq.component';
-
-@NgModule({
- imports: [
- BaseSharedModule,
- RouterModule.forChild([
- { path: '', component: FAQComponent }
- ])
- ],
- declarations: [
- FAQComponent
- ]
-})
-export default class FAQModule { }
diff --git a/Client/containers/bootstrap/bootstrap.component.html b/Client/containers/bootstrap/bootstrap.component.html
deleted file mode 100644
index 4f201118..00000000
--- a/Client/containers/bootstrap/bootstrap.component.html
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
Ng2-bootstrap Demo:
-
-
- Here we're using Bootstrap via ng2-bootstrap, which can even be rendered on the server!
-
-
-
-
-
-
-
Bootstrap Accordion demo:
-
-
-
-
-
-
-
-
-
-
-
-
-
- This content is straight in the template.
-
-
- {{ group?.content }}
-
-
-
The body of the accordion group grows to fit the contents
-
-
{{item}}
-
-
-
- I can have markup, too!
-
-
- This is just some content to illustrate fancy headings.
-
-
\ No newline at end of file
diff --git a/Client/containers/bootstrap/bootstrap.component.ts b/Client/containers/bootstrap/bootstrap.component.ts
deleted file mode 100644
index 52055259..00000000
--- a/Client/containers/bootstrap/bootstrap.component.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-bootstrap',
- templateUrl: './bootstrap.component.html'
-})
-export class BootstrapComponent {
-
- public oneAtATime: boolean = true;
- public items: Array = ['Item 1', 'Item 2', 'Item 3'];
-
- public status: Object = {
- isFirstOpen: true,
- isFirstDisabled: false
- };
-
- public groups: Array = [
- {
- title: 'Dynamic Group Header - 1',
- content: 'Dynamic Group Body - 1'
- },
- {
- title: 'Dynamic Group Header - 2',
- content: 'Dynamic Group Body - 2'
- }
- ];
-
- // Use "constructor"s only for dependency injection
- constructor () {}
-
- addItem(): void {
- this.items.push(`Items ${this.items.length + 1}`);
- }
-
-}
diff --git a/Client/containers/chat/chat.component.html b/Client/containers/chat/chat.component.html
deleted file mode 100644
index db685896..00000000
--- a/Client/containers/chat/chat.component.html
+++ /dev/null
@@ -1 +0,0 @@
-SignalR Chat example will go here
\ No newline at end of file
diff --git a/Client/containers/chat/chat.component.ts b/Client/containers/chat/chat.component.ts
deleted file mode 100644
index fedbca96..00000000
--- a/Client/containers/chat/chat.component.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Component, OnInit, Inject } from '@angular/core';
-
-
-@Component({
- selector: 'app-chat',
- templateUrl: './chat.component.html'
-})
-export class ChatComponent implements OnInit {
- // Use "constructor"s only for dependency injection
- constructor (@Inject('isBrowser') private isBrowser: boolean) {
- // Example of how to Inject the isBrowser/isNode we injected in our app.browser & app.server NgModules
- }
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit () {
- console.log('Are we inside the Browser ? ' + this.isBrowser);
- }
-}
diff --git a/Client/containers/home/home.component.html b/Client/containers/home/home.component.html
deleted file mode 100644
index a8582387..00000000
--- a/Client/containers/home/home.component.html
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
ASP.NET Core 1.1 :: (Currently VS2015 only) - .csproj 2017 version coming when closer to release
-
- Angular 2.* front-end UI framework
-
-
Universal - server-side rendering for SEO, deep-linking, and incredible performance.
-
NgRx - Redux architecture
-
HMR State Management - Don't lose your applications state during HMR!
-
AoT (Ahead-of-time) production compilation for even faster prod builds. (UPCOMING)
-
-
-
- The latest TypeScript 2.* features
-
-
- "Path" support example - create your own custom directory paths to avoid `../../` directory diving.
- Check the tsconfig to see how they are setup.
-
-
-
-
- Webpack 2 (current in Beta)
-
-
TS2 aware path support
-
Hot Module Reloading/Replacement for an amazing dev experience.
-
Tree-shaking (UPCOMING)
-
-
-
Bootstrap (ng2-bootstrap) : Bootstrap capable of being rendered even on the server.
\ No newline at end of file
diff --git a/Client/containers/home/home.component.spec.ts b/Client/containers/home/home.component.spec.ts
deleted file mode 100644
index 1b66832d..00000000
--- a/Client/containers/home/home.component.spec.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-///
-import { assert } from 'chai';
-import { DebugElement } from '@angular/core';
-import { By } from '@angular/platform-browser';
-import { TestBed, async, ComponentFixture } from '@angular/core/testing';
-import { HomeComponent } from './home.component';
-
-describe('Home Component', () => {
-
- let comp: HomeComponent;
- let fixture: ComponentFixture;
- let de: DebugElement;
- let el: HTMLElement;
-
- beforeEach(() => {
-
- TestBed.configureTestingModule({
- declarations: [HomeComponent]
- });
-
- fixture = TestBed.createComponent(HomeComponent);
-
- // HomeComponent test instance
- comp = fixture.componentInstance;
-
- fixture.detectChanges();
- });
-
- it('should display a title', async(() => {
- // query for the title
by CSS element selector
- de = fixture.debugElement.query(By.css('h1'));
-
- const titleText = de.nativeElement.textContent;
- expect(titleText).toContain(comp.title);
- }));
-
-});
diff --git a/Client/containers/home/home.component.ts b/Client/containers/home/home.component.ts
deleted file mode 100644
index d6245e78..00000000
--- a/Client/containers/home/home.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Component, OnInit, Inject } from '@angular/core';
-import { isBrowser } from 'angular2-universal';
-
-@Component({
- selector: 'app-home',
- templateUrl: './home.component.html'
-})
-export class HomeComponent implements OnInit {
-
- title: string = 'Angular2 Universal & ASP.NET Core advanced starter-kit';
-
- // Use "constructor"s only for dependency injection
- constructor () {
- }
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit () {
- console.log('Are we inside the Browser ? ' + isBrowser);
- }
-}
diff --git a/Client/containers/index.ts b/Client/containers/index.ts
deleted file mode 100644
index 6de4cb1f..00000000
--- a/Client/containers/index.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-
-/*
- This is our "Barrels" index (in this folder)
- Here we can just export all individual things
-
- We're also using TypeScript2's new "paths" to create non-directory import locations
- So instead of having to do something crazy like: "from '../../containers/'"
-
- We can just do:
- import { HomeComponent } from 'app-containers';
-
- Makes life easier!
-*/
-
-export * from './rest-test/rest-test.component';
-export * from './home/home.component';
-export * from './bootstrap/bootstrap.component';
-export * from './login/login.component';
-export * from './platform-examples/examples.component';
-export * from './not-found/not-found.component';
-export * from './chat/chat.component';
diff --git a/Client/containers/login/login.component.ts b/Client/containers/login/login.component.ts
deleted file mode 100644
index d6cb1c4d..00000000
--- a/Client/containers/login/login.component.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Router } from '@angular/router';
-import { Store } from '@ngrx/store';
-import { Component, OnInit } from '@angular/core';
-
-import { AppState, LOGIN_USER } from 'app';
-
-// Demo model
-export class UserModel {
- username: string;
- password: string;
-}
-
-@Component({
- selector: 'app-login',
- template: `
-
- `
-})
-export class LoginComponent implements OnInit {
-
- user: UserModel = new UserModel();
-
- // Use "constructor"s only for dependency injection
- constructor(private router: Router, private store: Store) {}
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit () {
-
- }
-
- submitUser () {
-
- this.store.dispatch({
- type: LOGIN_USER,
- payload: this.user
- });
-
- this.router.navigate(['/']);
- }
-
-}
diff --git a/Client/containers/not-found/not-found.component.css b/Client/containers/not-found/not-found.component.css
deleted file mode 100644
index 7fa55148..00000000
--- a/Client/containers/not-found/not-found.component.css
+++ /dev/null
@@ -1,3 +0,0 @@
-:host(.span12) {
- color: red;
-}
\ No newline at end of file
diff --git a/Client/containers/not-found/not-found.component.html b/Client/containers/not-found/not-found.component.html
deleted file mode 100644
index 65d5ee4f..00000000
--- a/Client/containers/not-found/not-found.component.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
Page Not Found Error 404
-
-
The page you requested could not be found, either contact your webmaster or try again. Use your browsers Back button to navigate to the page you have prevously come from
\ No newline at end of file
diff --git a/Client/containers/not-found/not-found.component.ts b/Client/containers/not-found/not-found.component.ts
deleted file mode 100644
index 664ab6c1..00000000
--- a/Client/containers/not-found/not-found.component.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Component, OnInit, Inject } from '@angular/core';
-
-
-@Component({
- selector: 'app-not-found',
- templateUrl: './not-found.component.html',
- styleUrls: ['./not-found.component.css']
-})
-export class NotFoundComponent implements OnInit {
- // Use "constructor"s only for dependency injection
- constructor (@Inject('isBrowser') private isBrowser: boolean) {
- // Example of how to Inject the isBrowser/isNode we injected in our app.browser & app.server NgModules
- }
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit () {
- console.log('Are we inside the Browser ? ' + this.isBrowser);
- }
-}
diff --git a/Client/containers/platform-examples/examples.component.ts b/Client/containers/platform-examples/examples.component.ts
deleted file mode 100644
index ab0efdf5..00000000
--- a/Client/containers/platform-examples/examples.component.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { isBrowser } from 'angular2-universal';
-import { Observable } from 'rxjs/Rx';
-import { StorageService } from 'app-shared';
-
-@Component({
- selector: 'app-examples',
- template: `
-
Platform specific examples
-
-
- With Universal, we can decide which logic we want to run in "what" situation (or platform).
- Certain things, we don't want running on the server:
-
- timeouts, intervals, references to window/document/navigator, etc...
-
-
- We want to use platform detecting logic from Universal such as isBrowser | isNode to wrap
- around these things.
-
-
-
Counter example = {{ browserPlatformInterval | async}}
-
-
- We can't let an "interval" run on the server, for it would loop endlessly and we'd never get a paint.
- In a situation like this we used "isBrowser" from Universal to wrap our logic per platform.
-
- `
-})
-export class ExamplesComponent implements OnInit {
-
- browserPlatformInterval: Observable;
-
- // Use "constructor"s only for dependency injection
- constructor (private storage: StorageService) {}
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit () {
- /*
- * Platform specific -Example- ::
- * We don't want an interval to continuously run on the server (it will just timeout the server) or delay our response.
- * So avoid having things like "interval" run on the server. With Universal's "isBrowser" we can have it only run there.
- */
- if (isBrowser) {
- this.browserPlatformInterval = Observable.interval(300);
- }
-
- // ************
- // Now let's play around with "Storage", but on a Platform level
- // Node will use in memory variable, while the Browser uses "localStorage"
- // Both are abstracted out use Dependency Injection to provide different classes for each use-case.
- // [high-five] :)
-
- this.storage.setItem('test', 'This came from Storage within each Platform !!');
-
- let storedItem = this.storage.getItem('test');
-
- console.log('Platform [Storage] test :: ' + storedItem);
- }
-
-}
diff --git a/Client/containers/rest-test/rest-test.component.html b/Client/containers/rest-test/rest-test.component.html
deleted file mode 100644
index 7a78d21e..00000000
--- a/Client/containers/rest-test/rest-test.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
This is a RestAPI Example (hitting WebAPI in our case)
-
-
- Let's get some fake users from Rest:
- You can find the Web API Routes in {{ "/Server/Api/ ... "}}
-
-
-
Loading...
-
-
-
-
-
User ID
-
Name
-
-
-
-
-
{{ user.id }}
-
{{ user.name }}
-
-
-
diff --git a/Client/containers/rest-test/rest-test.component.ts b/Client/containers/rest-test/rest-test.component.ts
deleted file mode 100644
index ed606227..00000000
--- a/Client/containers/rest-test/rest-test.component.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import {
- Component, OnInit,
- // animation imports
- trigger, state, style, transition, animate } from '@angular/core';
-
-import { Http } from '@angular/http';
-import { HttpCacheService } from 'app-shared';
-
-@Component({
- selector: 'app-rest-test',
- templateUrl: './rest-test.component.html',
- animations: [
- // Animation example
- // Triggered in the ngFor with [@flyInOut]
- trigger('flyInOut', [
- state('in', style({transform: 'translateY(0)'})),
- transition('void => *', [
- style({transform: 'translateY(-100%)'}),
- animate(1000)
- ]),
- transition('* => void', [
- animate(1000, style({transform: 'translateY(100%)'}))
- ])
- ])
- ]
-})
-export class RestTestComponent implements OnInit {
-
- public users: IUser[];
-
- // Use "constructor"s only for dependency injection
- constructor(private httpCache: HttpCacheService) { }
-
- // Here you want to handle anything with @Input()'s @Output()'s
- // Data retrieval / etc - this is when the Component is "ready" and wired up
- ngOnInit() {
- this.httpCache.get('/api/test/users').subscribe(result => {
- this.users = result;
- });
- }
-}
-
-interface IUser {
- id: number;
- name: string;
-}
diff --git a/Client/karma-tests.ts b/Client/karma-tests.ts
deleted file mode 100644
index 69c52cfa..00000000
--- a/Client/karma-tests.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-// Load required polyfills and testing libraries
-import 'angular2-universal-polyfills';
-import 'zone.js/dist/long-stack-trace-zone';
-import 'zone.js/dist/proxy.js';
-import 'zone.js/dist/sync-test';
-import 'zone.js/dist/jasmine-patch';
-import 'zone.js/dist/async-test';
-import 'zone.js/dist/fake-async-test';
-import * as testing from '@angular/core/testing';
-import * as testingBrowser from '@angular/platform-browser-dynamic/testing';
-
-// There's no typing for the `__karma__` variable. Just declare it as any
-declare var __karma__: any;
-declare var require: any;
-
-// Prevent Karma from running prematurely
-__karma__.loaded = function () {};
-
-// First, initialize the Angular testing environment
-testing.getTestBed().initTestEnvironment(
- testingBrowser.BrowserDynamicTestingModule,
- testingBrowser.platformBrowserDynamicTesting()
-);
-
-// Then we find all the tests
-const context = require.context('./', true, /\.spec\.ts$/);
-
-// And load the modules
-context.keys().map(context);
-
-// Finally, start Karma to run the tests
-__karma__.start();
diff --git a/Client/shared/cache/api.ts b/Client/shared/cache/api.ts
deleted file mode 100644
index d656e139..00000000
--- a/Client/shared/cache/api.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Injectable } from '@angular/core';
-import { Http, RequestOptionsArgs } from '@angular/http';
-import { Observable } from 'rxjs/Observable';
-import { isNode } from 'angular2-universal';
-
-import 'rxjs/add/observable/throw';
-import 'rxjs/add/observable/of';
-import 'rxjs/add/operator/do';
-import 'rxjs/add/operator/share';
-
-import { CacheService } from './universal-cache';
-
-
-@Injectable()
-export class HttpCacheService {
-
- constructor(public _http: Http, public _cache: CacheService) { }
-
- get(url, options?: RequestOptionsArgs, autoClear: boolean = true) {
-
- // You want to return the cache if there is a response in it.
- // This would cache the first response so if your API isn't idempotent you probably want to
- // remove the item from the cache after you use it. LRU of 1
- let key = url;
-
- if (this._cache.has(key)) {
-
- const cachedResponse = this._cache.get(key);
-
- // if autoClear is set to false, item will stay in cache until you manually clear it
- // ie: trigger CacheService.remove(url /* with the url/key used here */)
-
- if (autoClear) {
- // remove previous value automatically for now
- this._cache.remove(key);
- }
-
- return Observable.of(cachedResponse);
- }
-
- // note: you probably shouldn't .share() and you should write the correct logic
-
- return this._http.get(url, options)
- .map(res => res.json())
- .do(json => { if (isNode) { this._cache.set(key, json); } })
- .share();
- }
-}
diff --git a/Client/shared/cache/storage/index.ts b/Client/shared/cache/storage/index.ts
deleted file mode 100644
index 5415b5fa..00000000
--- a/Client/shared/cache/storage/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-
-export * from './storage.browser';
-export * from './storage.node';
-export * from './storage.service';
diff --git a/Client/shared/cache/storage/storage.browser.ts b/Client/shared/cache/storage/storage.browser.ts
deleted file mode 100644
index 5f96bd19..00000000
--- a/Client/shared/cache/storage/storage.browser.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { StorageService } from './storage.service';
-
-export class BrowserStorage implements StorageService {
- getItem (key: string): any {
- let storedItem = window.localStorage.getItem(key);
- try {
- return JSON.parse(storedItem);
- } catch (ex) {
- return storedItem;
- }
- }
-
- setItem (key: string, value: any) {
- // We need to try and stringify it first (we can't save Objects/etc or it'll error out)
- if (typeof value !== 'string') {
- window.localStorage.setItem(key, JSON.stringify(value));
- } else {
- window.localStorage.setItem(key, value);
- }
- }
-
- removeItem (key: string) {
- window.localStorage.removeItem(key);
- }
-
- clear () {
- window.localStorage.clear();
- }
-}
diff --git a/Client/shared/cache/storage/storage.node.ts b/Client/shared/cache/storage/storage.node.ts
deleted file mode 100644
index 651ce21b..00000000
--- a/Client/shared/cache/storage/storage.node.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { StorageService } from './storage.service';
-
-// Our pseudo server Storage we'll use on the server to replace localStorage
-// This can handle most scenarios, but if you truly needed a server Memory Cache
-// you'd want to use Redis or something here
-let fakeInMemoryStore = {};
-
-export class ServerStorage implements StorageService {
- getItem(key) {
- return fakeInMemoryStore[key] || undefined;
- }
- setItem(key, value) {
- return fakeInMemoryStore[key] = value;
- }
- removeItem(key) {
- try {
- delete fakeInMemoryStore[key];
- } catch (ex) { }
- }
- clear() {
- fakeInMemoryStore = {};
- }
-}
diff --git a/Client/shared/cache/storage/storage.service.ts b/Client/shared/cache/storage/storage.service.ts
deleted file mode 100644
index 5f02cac0..00000000
--- a/Client/shared/cache/storage/storage.service.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Injectable } from '@angular/core';
-
-// This is our "shell" Service that will be used through out the application
-
-// Each "platform" will inject its own implementation (storage.browser & storage.node)
-// Browser is using window.localStorage
-// Node is using inMemory variable Object (which can work for most scenarios),
-// If you needed a true cache you'd need to implement Redis or similar here.
-
-// Useage within the app:
-// constructor (private storage: StorageService) {}
-
-// this.storage.setItem('someKey', 123);
-
-@Injectable()
-export class StorageService {
- getItem = (key) : any => {};
- setItem = (key, value) => {};
- removeItem = (key) => {};
- clear = () => {};
-}
diff --git a/Client/shared/cache/universal-cache.ts b/Client/shared/cache/universal-cache.ts
deleted file mode 100644
index c390aeb0..00000000
--- a/Client/shared/cache/universal-cache.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { Injectable, isDevMode } from '@angular/core';
-
-@Injectable()
-export class CacheService {
- static KEY = 'CacheService';
- _cache = new Map();
-
- /**
- * check if there is a value in our store
- */
- has(key: string | number): boolean {
- let _key = this.normalizeKey(key);
- return this._cache.has(_key);
- }
-
- /**
- * store our state
- */
- set(key: string | number, value: any): void {
- let _key = this.normalizeKey(key);
- this._cache.set(_key, value);
- }
-
- /**
- * get our cached value
- */
- get(key: string | number): any {
- let _key = this.normalizeKey(key);
- return this._cache.get(_key);
- }
-
- /**
- * remove specific cache item
- */
- remove(key: string | number): boolean {
- let _key = this.normalizeKey(key);
- if (_key && this._cache.has(_key)) {
- this._cache.delete(_key);
- return true;
- }
- return false;
- }
-
- /**
- * release memory refs
- */
- clear(): void {
- this._cache.clear();
- }
-
- /**
- * convert to json for the client
- */
- dehydrate(): any {
- let json = {};
- this._cache.forEach((value: any, key: string) => json[key] = value);
- return json;
- }
-
- /**
- * convert server json into out initial state
- */
- rehydrate(json: any): void {
- Object.keys(json).forEach((key: string) => {
- let _key = this.normalizeKey(key);
- let value = json[_key];
- this._cache.set(_key, value);
- });
- }
-
- /**
- * allow JSON.stringify to work
- */
- toJSON(): any {
- return this.dehydrate();
- }
-
- /**
- * convert numbers into strings
- */
- normalizeKey(key: string | number): string {
- if (isDevMode() && this._isInvalidValue(key)) {
- throw new Error('Please provide a valid key to save in the CacheService');
- }
-
- return key + '';
- }
-
- _isInvalidValue(key): boolean {
- return key === undefined ||
- key === undefined ||
- key === 0 ||
- key === '' ||
- typeof key === 'boolean' ||
- Number.isNaN(key);
- }
-}
diff --git a/Client/shared/http/http-gateway.service.ts b/Client/shared/http/http-gateway.service.ts
deleted file mode 100644
index ae119300..00000000
--- a/Client/shared/http/http-gateway.service.ts
+++ /dev/null
@@ -1,224 +0,0 @@
-// ** HttpGateway **
-
-import 'rxjs/add/operator/map';
-import 'rxjs/add/operator/catch';
-import 'rxjs/add/operator/finally';
-
-import { Injectable } from '@angular/core';
-import { Router } from '@angular/router';
-import { Http, Response, RequestOptions, RequestMethod, URLSearchParams } from '@angular/http';
-import { Observable } from 'rxjs/Observable';
-import { Subject } from 'rxjs/Subject';
-
-export class ApiGatewayOptions {
- method: RequestMethod;
- url: string;
- headers: any = {};
- params = {};
- data = {};
-}
-
-@Injectable()
-export class ApiGatewayService {
-
- // Define the internal Subject we'll use to push the command count
- private pendingCommandsSubject = new Subject();
- private pendingCommandCount = 0;
-
- // Provide the *public* Observable that clients can subscribe to
- private pendingCommands$: Observable;
-
- constructor(private http: Http, private router: Router) {
- this.pendingCommands$ = this.pendingCommandsSubject.asObservable();
- }
-
- // Http overrides
- // -------------------
-
- get(url: string, params?: any): Observable {
- let options = new ApiGatewayOptions();
-
- this.addBearerToken(options);
-
- return this.http.get(url, options);
- }
-
- post(url: string, data?: any, params?: any): Observable {
- if (!data) {
- data = params;
- params = {};
- }
- let options = new ApiGatewayOptions();
- options.method = RequestMethod.Post;
- options.url = url;
- options.params = params;
- options.data = data;
- return this.request(options);
- }
-
- put(url: string, data?: any, params?: any): Observable {
- if (!data) {
- data = params;
- params = {};
- }
- let options = new ApiGatewayOptions();
- options.method = RequestMethod.Put;
- options.url = url;
- options.params = params;
- options.data = data;
- return this.request(options);
- }
-
- delete(url: string, params?: any): Observable {
- let options = new ApiGatewayOptions();
- options.method = RequestMethod.Delete;
- options.url = url;
- options.params = params;
- return this.request(options);
- }
-
-
- // Internal methods
- // --------------------
-
- private request(options: ApiGatewayOptions): Observable {
- options.method = (options.method || RequestMethod.Get);
- options.url = (options.url || '');
- options.headers = (options.headers || {});
- options.params = (options.params || {});
- options.data = (options.data || {});
-
- this.interpolateUrl(options);
- this.addXsrfToken(options);
- this.addContentType(options);
- this.addBearerToken(options);
-
- let requestOptions = new RequestOptions();
- requestOptions.method = options.method;
- requestOptions.url = options.url;
- requestOptions.headers = options.headers;
- requestOptions.search = this.buildUrlSearchParams(options.params);
- requestOptions.body = JSON.stringify(options.data);
-
- let isCommand = (options.method !== RequestMethod.Get);
-
- if (isCommand) {
- this.pendingCommandsSubject.next(++this.pendingCommandCount);
- }
-
- let stream = this.http.request(options.url, requestOptions)
- .catch((error: any) => {
- this.handleError(error);
- return Observable.throw(error);
- })
- .map(this.unwrapHttpValue)
- .catch((error: any) => {
- return Observable.throw(this.unwrapHttpError(error));
- })
- .finally(() => {
- if (isCommand) {
- this.pendingCommandsSubject.next(--this.pendingCommandCount);
- }
- });
-
- return stream;
- }
-
-
- private addContentType(options: ApiGatewayOptions): ApiGatewayOptions {
- if (options.method !== RequestMethod.Get) {
- options.headers['Content-Type'] = 'application/json; charset=UTF-8';
- }
- return options;
- }
-
- private addBearerToken(options: ApiGatewayOptions): ApiGatewayOptions {
- if (sessionStorage.getItem('accessToken')) {
- options.headers.Authorization = 'Bearer ' + sessionStorage.getItem('accessToken');
- }
- return options;
- }
-
- private extractValue(collection: any, key: string): any {
- let value = collection[key];
- delete (collection[key]);
- return value;
- }
-
- private addXsrfToken(options: ApiGatewayOptions): ApiGatewayOptions {
- let xsrfToken = this.getXsrfCookie();
- if (xsrfToken) {
- options.headers['X-XSRF-TOKEN'] = xsrfToken;
- }
- return options;
- }
-
- private getXsrfCookie(): string {
- let matches = document.cookie.match(/\bXSRF-TOKEN=([^\s;]+)/);
- try {
- return (matches && decodeURIComponent(matches[1]));
- } catch (decodeError) {
- return ('');
- }
- }
-
- private addCors(options: ApiGatewayOptions): ApiGatewayOptions {
- options.headers['Access-Control-Allow-Origin'] = '*';
- return options;
- }
-
- private buildUrlSearchParams(params: any): URLSearchParams {
- let searchParams = new URLSearchParams();
- for (let key in params) {
- if (params.hasOwnProperty(key)) {
- searchParams.append(key, params[key]);
- }
- }
- return searchParams;
- }
-
- private interpolateUrl(options: ApiGatewayOptions): ApiGatewayOptions {
- options.url = options.url.replace(/:([a-zA-Z]+[\w-]*)/g, ($0, token) => {
- // Try to move matching token from the params collection.
- if (options.params.hasOwnProperty(token)) {
- return (this.extractValue(options.params, token));
- }
- // Try to move matching token from the data collection.
- if (options.data.hasOwnProperty(token)) {
- return (this.extractValue(options.data, token));
- }
- // If a matching value couldn't be found, just replace
- // the token with the empty string.
- return ('');
- });
- // Clean up any repeating slashes.
- options.url = options.url.replace(/\/{2,}/g, '/');
- // Clean up any trailing slashes.
- options.url = options.url.replace(/\/+$/g, '');
-
- return options;
- }
-
- private handleError(error: any) {
- if (error.status === 401) {
- // use DI to abstract this
- // sessionStorage.clear();
- this.router.navigate(['/login']);
- }
- }
-
- private unwrapHttpError(error: any): any {
- try {
- return (error.json());
- } catch (jsonError) {
- return ({
- code: -1,
- message: 'An unexpected error occurred.'
- });
- }
- }
-
- private unwrapHttpValue(value: Response): any {
- return (value.json());
- }
-}
diff --git a/Client/shared/index.ts b/Client/shared/index.ts
deleted file mode 100644
index cb7d402d..00000000
--- a/Client/shared/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-
-/*
- This is our "Barrels" index (in this folder)
- Here we can just export all individual things
-
- We're also using TypeScript2's new "paths" to create non-directory import locations
- So instead of having to do something crazy like: "from '../../shared/'"
-
- We can just do:
- import { WebSocketService } from 'app-shared';
-
- Makes life easier!
-*/
-
-// Put Shared SERVICES here in /shared
-
-export * from './cache/api';
-export * from './cache/universal-cache';
-export * from './cache/storage';
-
-export * from './http/http-gateway.service';
-
-export * from './rx/rx-context.directive';
-
-export * from './meta/meta.service';
-export * from './meta/meta.store';
-
diff --git a/Client/shared/meta/meta.service.ts b/Client/shared/meta/meta.service.ts
deleted file mode 100644
index a82f55b4..00000000
--- a/Client/shared/meta/meta.service.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-
-import { Injectable, Inject } from '@angular/core';
-import { DomAdapter } from '@angular/platform-browser/src/dom/dom_adapter';
-import { __platform_browser_private__, DOCUMENT } from '@angular/platform-browser';
-import { isNode } from 'angular2-universal';
-
-/**
- * Represent meta element.
- *
- * ### Example
- *
- * ```ts
- * { name: 'application-name', content: 'Name of my application' },
- * { name: 'description', content: 'A description of the page', id: 'desc' }
- * // ...
- * // Twitter
- * { name: 'twitter:title', content: 'Content Title' }
- * // ...
- * // Google+
- * { itemprop: 'name', content: 'Content Title' },
- * { itemprop: 'description', content: 'Content Title' }
- * // ...
- * // Facebook / Open Graph
- * { property: 'fb:app_id', content: '123456789' },
- * { property: 'og:title', content: 'Content Title' }
- * ```
- *
- * @experimental
- */
-export interface MetaDefinition {
- charset?: string;
- content?: string;
- httpEquiv?: string;
- id?: string;
- itemprop?: string;
- name?: string;
- property?: string;
- scheme?: string;
- url?: string;
- [prop: string]: string;
-}
-
-/**
- * A service that can be used to get and add meta tags.
- *
- * @experimental
- */
-@Injectable()
-export class Meta {
- private _dom: DomAdapter = __platform_browser_private__.getDOM();
-
-
- constructor( @Inject(DOCUMENT) private _document: any) { }
-
- /**
- * Sets the title of the page
- */
- setTitle(title: string) {
- this._document.title = title;
- }
-
- /**
- * Adds a new meta tag to the dom.
- *
- * ### Example
- *
- * ```ts
- * const name: MetaDefinition = {name: 'application-name', content: 'Name of my application'};
- * const desc: MetaDefinition = {name: 'description', content: 'A description of the page'};
- * const tags: HTMLMetaElement[] = this.meta.addTags([name, desc]);
- * ```
- *
- * @param tags
- * @returns {HTMLMetaElement[]}
- */
- addTags(...tags: Array): HTMLMetaElement[] {
- if (isNode) { // Avoid readding the meta tags in client
- const presentTags = this._flattenArray(tags);
- if (presentTags.length === 0) return [];
- return presentTags.map((tag: MetaDefinition) => this._addInternal(tag));
- }
- }
-
- private _addInternal(tag: MetaDefinition): HTMLMetaElement {
- const meta: HTMLMetaElement = this._createMetaElement();
- this._prepareMetaElement(tag, meta);
- this._appendMetaElement(meta);
- return meta;
- }
-
- private _createMetaElement(): HTMLMetaElement {
- return this._dom.createElement('meta') as HTMLMetaElement;
- }
-
- private _prepareMetaElement(tag: MetaDefinition, el: HTMLMetaElement): HTMLMetaElement {
- Object.keys(tag).forEach((prop: string) => this._dom.setAttribute(el, prop, tag[prop]));
- return el;
- }
-
- private _appendMetaElement(meta: HTMLMetaElement): void {
- const head = this._document.head;
- this._dom.appendChild(head, meta);
- }
-
- private _flattenArray(input: any[], out: any[] = []): any[] {
- if (input) {
- for (let i = 0; i < input.length; i++) {
- const item: any = input[i];
- if (Array.isArray(item)) {
- this._flattenArray(item, out);
- } else if (item) {
- out.push(item);
- }
- }
- }
- return out;
- }
-}
diff --git a/Client/shared/meta/meta.store.ts b/Client/shared/meta/meta.store.ts
deleted file mode 100644
index b89fe260..00000000
--- a/Client/shared/meta/meta.store.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-export let metaStore = {
-
- title : '',
-
- meta : {
-
- },
-
- getState () {
- return {
- title: this.title,
- meta: this.meta
- };
- }
-};
diff --git a/Client/shared/rx/rx-context.directive.ts b/Client/shared/rx/rx-context.directive.ts
deleted file mode 100644
index 29badfec..00000000
--- a/Client/shared/rx/rx-context.directive.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import {
- Directive, Input, OnInit,
- EmbeddedViewRef, TemplateRef, ViewContainerRef, ChangeDetectorRef
-} from '@angular/core';
-
-import { Observable } from 'rxjs/Rx';
-
-/* tslint:disable */ // <-- disabled to ignore app- prefix
-@Directive({
- selector: '[rxContext][apprxContextOn]'
-})
-/* tslint:enable */
-export class RxContextDirective implements OnInit {
-
- @Input() rxContextOn: Observable;
- viewRef: EmbeddedViewRef;
-
- constructor (private templateRef: TemplateRef, private vcr: ViewContainerRef, private cdr: ChangeDetectorRef) {
- this.cdr.detach();
- }
-
- ngOnInit() {
- this.rxContextOn.subscribe(state => {
- if (!this.viewRef) {
- this.viewRef = this.vcr.createEmbeddedView(this.templateRef, { '$implicit' : state });
- }
- this.viewRef.context.$implicit = state;
- });
- }
-
-}
diff --git a/Client/typings.d.ts b/Client/typings.d.ts
deleted file mode 100644
index 23eda4ca..00000000
--- a/Client/typings.d.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Custom Type Definitions
- * When including 3rd party modules you also need to include the type definition for the module
- * if they don't provide one within the module. You can try to install it with typings
-typings install node --save
- * If you can't find the type definition in the registry we can make an ambient definition in
- * this file for now. For example
-declare module "my-module" {
- export function doesSomething(value: string): string;
-}
- *
- * If you're prototying and you will fix the types later you can also declare it as type any
- *
-declare var assert: any;
- *
- * If you're importing a module that uses Node.js modules which are CommonJS you need to import as
- *
-import * as _ from 'lodash'
- * You can include your type definitions in this file until you create one for the typings registry
- * see https://github.com/typings/registry
- *
- */
-
-// declare module '*'; // default type definitions for any for modules that are not found.
-// caveat: if this is enabled and you do not have the proper module there may not be an error.
-// suggestion: follow the pattern below with modern-lru which provides an alternative way to create an 'any' module.
-
-// for legacy tslint etc to understand
-declare module 'modern-lru' {
- let x: any;
- export = x;
-}
-
-declare var System: SystemJS;
-
-interface SystemJS {
- import: (path?: string) => Promise;
-}
-
-// Extra variables that live on Global that will be replaced by webpack DefinePlugin
-declare var ENV: string;
-declare var HMR: boolean;
-// declare var Zone: {current: any};
-interface GlobalEnvironment {
- ENV;
- HMR;
-}
-
-interface WebpackModule {
- hot: {
- data?: any,
- idle: any,
- accept(dependencies?: string | string[], callback?: (updatedDependencies?: any) => void): void;
- decline(dependencies?: string | string[]): void;
- dispose(callback?: (data?: any) => void): void;
- addDisposeHandler(callback?: (data?: any) => void): void;
- removeDisposeHandler(callback?: (data?: any) => void): void;
- check(autoApply?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void;
- apply(options?: any, callback?: (err?: Error, outdatedModules?: any[]) => void): void;
- status(callback?: (status?: string) => void): void | string;
- removeStatusHandler(callback?: (status?: string) => void): void;
- };
-}
-
-interface WebpackRequire {
- context(file: string, flag?: boolean, exp?: RegExp): any;
-}
-
-// Extend typings
-interface NodeRequire extends WebpackRequire {}
-interface NodeModule extends WebpackModule {}
-interface Global extends GlobalEnvironment {}
diff --git a/ClientApp/app/_styles.scss b/ClientApp/app/_styles.scss
new file mode 100644
index 00000000..87ea278f
--- /dev/null
+++ b/ClientApp/app/_styles.scss
@@ -0,0 +1,49 @@
+$body-bg: #f1f1f1;
+$body-color: #111;
+$theme-colors: (
+ 'primary': #216dad
+);
+$theme-colors: (
+ 'accent': #669ecd
+);
+
+@import 'http://github.com/~bootstrap/scss/bootstrap';
+.panel {
+ box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),
+ 0 1px 5px 0 rgba(0, 0, 0, 0.12);
+ height: 100%;
+ flex: 1;
+ background-color: rgba(255, 255, 255, 0.3);
+ border-radius: 0.25rem;
+ .title {
+ background-color: #86afd0;
+ color: #ffffff;
+ text-align: center;
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+ }
+ .body {
+ display: flex;
+ }
+}
+
+@include media-breakpoint-down(md) {
+ .panel {
+ .body {
+ flex-direction: column;
+ padding: 0px;
+ }
+ .title {
+ font-size: 1.5rem;
+ }
+ }
+}
+
+@include media-breakpoint-up(md) {
+ .panel {
+ .body {
+ flex-direction: row;
+ padding: 1.5rem;
+ }
+ }
+}
diff --git a/ClientApp/app/_variables.scss b/ClientApp/app/_variables.scss
new file mode 100644
index 00000000..313015eb
--- /dev/null
+++ b/ClientApp/app/_variables.scss
@@ -0,0 +1,6 @@
+@import 'http://github.com/styles';
+$header-height: 50px;
+$menu-max-width: 25%;
+$navbar-default-bg: #312312;
+$light-orange: #ff8c00;
+$navbar-default-color: $light-orange;
diff --git a/ClientApp/app/app.component.html b/ClientApp/app/app.component.html
new file mode 100644
index 00000000..45ca7f89
--- /dev/null
+++ b/ClientApp/app/app.component.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/ClientApp/app/app.component.scss b/ClientApp/app/app.component.scss
new file mode 100644
index 00000000..1e7eea64
--- /dev/null
+++ b/ClientApp/app/app.component.scss
@@ -0,0 +1,71 @@
+@import 'http://github.com/variables';
+/* *** Overall APP Styling can go here ***
+ --------------------------------------------
+ Note: This Component has ViewEncapsulation.None so the styles will bleed out
+
+*/
+
+body {
+ line-height: 18px;
+ padding-top: $header-height;
+}
+
+.body-content {
+ margin: auto;
+}
+
+h1 {
+ border-bottom: 3px theme-color('accent') solid;
+ font-size: 24px;
+}
+
+h2 {
+ font-size: 20px;
+}
+
+h1,
+h2,
+h3 {
+ padding: 3px 0;
+}
+
+ul {
+ padding: 10px 25px;
+}
+
+ul li {
+ padding: 5px 0;
+}
+
+blockquote {
+ margin: 25px 10px;
+ padding: 10px 35px 10px 10px;
+ border-left: 10px color('green') solid;
+ background: $gray-100;
+}
+
+blockquote a,
+blockquote a:hover {
+ color: $green;
+}
+
+@include media-breakpoint-up(lg) {
+ body {
+ padding-top: 30px;
+ }
+ .body-content {
+ margin-left: $menu-max-width;
+ }
+ h1 {
+ border-bottom: 5px #4189c7 solid;
+ font-size: 36px;
+ }
+ h2 {
+ font-size: 30px;
+ }
+ h1,
+ h2,
+ h3 {
+ padding: 10px 0;
+ }
+}
diff --git a/ClientApp/app/app.component.ts b/ClientApp/app/app.component.ts
new file mode 100644
index 00000000..415c196a
--- /dev/null
+++ b/ClientApp/app/app.component.ts
@@ -0,0 +1,103 @@
+import {
+ Component,
+ Injector,
+ OnDestroy,
+ OnInit,
+ ViewEncapsulation
+} from '@angular/core';
+import { Meta, Title } from '@angular/platform-browser';
+import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
+import { REQUEST } from '@nguniversal/aspnetcore-engine/tokens';
+// i18n support
+import { TranslateService } from '@ngx-translate/core';
+import { Subscription } from 'rxjs';
+import { filter, map, mergeMap } from 'rxjs/operators';
+import { LinkService } from './shared/link.service';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class AppComponent implements OnInit, OnDestroy {
+ // This will go at the END of your title for example "Home - Angular Universal..." <-- after the dash (-)
+ private endPageTitle: string = 'Angular Universal and ASP.NET Core Starter';
+ // If no Title is provided, we'll use a default one before the dash(-)
+ private defaultPageTitle: string = 'My App';
+
+ private routerSub$: Subscription;
+ private request;
+
+ constructor(
+ private router: Router,
+ private activatedRoute: ActivatedRoute,
+ private title: Title,
+ private meta: Meta,
+ private linkService: LinkService,
+ public translate: TranslateService,
+ private injector: Injector
+ ) {
+ // this language will be used as a fallback when a translation isn't found in the current language
+ translate.setDefaultLang('en');
+
+ // the lang to use, if the lang isn't available, it will use the current loader to get them
+ translate.use('en');
+
+ this.request = this.injector.get(REQUEST);
+
+ console.log(`What's our REQUEST Object look like?`);
+ console.log(
+ `The Request object only really exists on the Server, but on the Browser we can at least see Cookies`
+ );
+ console.log(this.request);
+ }
+
+ ngOnInit() {
+ // Change "Title" on every navigationEnd event
+ // Titles come from the data.title property on all Routes (see app.routes.ts)
+ this._changeTitleOnNavigation();
+ }
+
+ ngOnDestroy() {
+ // Subscription clean-up
+ this.routerSub$.unsubscribe();
+ }
+
+ private _changeTitleOnNavigation() {
+ this.routerSub$ = this.router.events
+ .pipe(
+ filter(event => event instanceof NavigationEnd),
+ map(() => this.activatedRoute),
+ map(route => {
+ while (route.firstChild) route = route.firstChild;
+ return route;
+ }),
+ filter(route => route.outlet === 'primary'),
+ mergeMap(route => route.data)
+ )
+ .subscribe(event => {
+ this._setMetaAndLinks(event);
+ });
+ }
+
+ private _setMetaAndLinks(event) {
+ // Set Title if available, otherwise leave the default Title
+ const title = event['title']
+ ? `${event['title']} - ${this.endPageTitle}`
+ : `${this.defaultPageTitle} - ${this.endPageTitle}`;
+
+ this.title.setTitle(title);
+
+ const metaData = event['meta'] || [];
+ const linksData = event['links'] || [];
+
+ for (let i = 0; i < metaData.length; i++) {
+ this.meta.updateTag(metaData[i]);
+ }
+
+ for (let i = 0; i < linksData.length; i++) {
+ this.linkService.addTag(linksData[i]);
+ }
+ }
+}
diff --git a/ClientApp/app/app.module.browser.ts b/ClientApp/app/app.module.browser.ts
new file mode 100644
index 00000000..5162c3ca
--- /dev/null
+++ b/ClientApp/app/app.module.browser.ts
@@ -0,0 +1,40 @@
+import { NgModule } from '@angular/core';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { ORIGIN_URL, REQUEST } from '@nguniversal/aspnetcore-engine/tokens';
+import { PrebootModule } from 'preboot';
+import { AppComponent } from './app.component';
+import { AppModuleShared } from './app.module';
+
+export function getOriginUrl() {
+ return window.location.origin;
+}
+
+export function getRequest() {
+ // the Request object only lives on the server
+ return { cookie: document.cookie };
+}
+
+@NgModule({
+ bootstrap: [AppComponent],
+ imports: [
+ PrebootModule.withConfig({ appRoot: 'app-root' }),
+ BrowserAnimationsModule,
+
+ // Our Common AppModule
+ AppModuleShared
+ ],
+ providers: [
+ {
+ // We need this for our Http calls since they'll be using an ORIGIN_URL provided in main.server
+ // (Also remember the Server requires Absolute URLs)
+ provide: ORIGIN_URL,
+ useFactory: getOriginUrl
+ },
+ {
+ // The server provides these in main.server
+ provide: REQUEST,
+ useFactory: getRequest
+ }
+ ]
+})
+export class AppModule {}
diff --git a/ClientApp/app/app.module.server.ts b/ClientApp/app/app.module.server.ts
new file mode 100644
index 00000000..06af629a
--- /dev/null
+++ b/ClientApp/app/app.module.server.ts
@@ -0,0 +1,28 @@
+import { NgModule } from '@angular/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { ServerModule } from '@angular/platform-server';
+import { PrebootModule } from 'preboot';
+import { AppComponent } from './app.component';
+import { AppModuleShared } from './app.module';
+
+import { TransferHttpCacheModule, StateTransferInitializerModule } from '@nguniversal/common';
+
+@NgModule({
+ bootstrap: [AppComponent],
+ imports: [
+ // Our Common AppModule
+ AppModuleShared,
+
+ ServerModule,
+ PrebootModule.withConfig({ appRoot: 'app-root' }),
+ NoopAnimationsModule,
+
+ TransferHttpCacheModule, // still needs fixes for 5.0
+ // Leave this commented out for now, as it breaks Server-renders
+ // Looking into fixes for this! - @MarkPieszak
+ // StateTransferInitializerModule // <-- broken for the time-being with ASP.NET
+ ]
+})
+export class AppModule {
+ constructor() {}
+}
diff --git a/ClientApp/app/app.module.ts b/ClientApp/app/app.module.ts
new file mode 100644
index 00000000..a30f46d8
--- /dev/null
+++ b/ClientApp/app/app.module.ts
@@ -0,0 +1,212 @@
+import { CommonModule } from '@angular/common';
+import { HttpClient, HttpClientModule } from '@angular/common/http';
+import { NgModule } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import {
+ BrowserModule,
+ BrowserTransferStateModule
+} from '@angular/platform-browser';
+import { PreloadAllModules, RouterModule } from '@angular/router';
+import { ORIGIN_URL } from '@nguniversal/aspnetcore-engine/tokens';
+import { TransferHttpCacheModule } from '@nguniversal/common';
+// i18n support
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { TranslateHttpLoader } from '@ngx-translate/http-loader';
+import { AccordionModule } from 'ngx-bootstrap';
+import { AppComponent } from './app.component';
+import { NavMenuComponent } from './components/navmenu/navmenu.component';
+import { UserDetailComponent } from './components/user-detail/user-detail.component';
+import { CounterComponent } from './containers/counter/counter.component';
+import { HomeComponent } from './containers/home/home.component';
+import { NgxBootstrapComponent } from './containers/ngx-bootstrap-demo/ngx-bootstrap.component';
+import { NotFoundComponent } from './containers/not-found/not-found.component';
+import { UsersComponent } from './containers/users/users.component';
+import { LinkService } from './shared/link.service';
+import { UserService } from './shared/user.service';
+
+export function createTranslateLoader(http: HttpClient, baseHref) {
+ // Temporary Azure hack
+ if (baseHref === null && typeof window !== 'undefined') {
+ baseHref = window.location.origin;
+ }
+ // i18n files are in `wwwroot/assets/`
+ return new TranslateHttpLoader(http, `${baseHref}/assets/i18n/`, '.json');
+}
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ NavMenuComponent,
+ CounterComponent,
+ UsersComponent,
+ UserDetailComponent,
+ HomeComponent,
+ NotFoundComponent,
+ NgxBootstrapComponent
+ ],
+ imports: [
+ CommonModule,
+ BrowserModule.withServerTransition({
+ appId: 'my-app-id' // make sure this matches with your Server NgModule
+ }),
+ HttpClientModule,
+ TransferHttpCacheModule,
+ BrowserTransferStateModule,
+ FormsModule,
+ ReactiveFormsModule,
+ AccordionModule.forRoot(), // You could also split this up if you don't want the Entire Module imported
+
+ // i18n support
+ TranslateModule.forRoot({
+ loader: {
+ provide: TranslateLoader,
+ useFactory: createTranslateLoader,
+ deps: [HttpClient, [ORIGIN_URL]]
+ }
+ }),
+
+ // App Routing
+ RouterModule.forRoot(
+ [
+ {
+ path: '',
+ redirectTo: 'home',
+ pathMatch: 'full'
+ },
+ {
+ path: 'home',
+ component: HomeComponent,
+
+ // *** SEO Magic ***
+ // We're using "data" in our Routes to pass in our tag information
+ // Note: This is only happening for ROOT level Routes, you'd have to add some additional logic if you wanted this for Child level routing
+ // When you change Routes it will automatically append these to your document for you on the Server-side
+ // - check out app.component.ts to see how it's doing this
+ data: {
+ title: 'Homepage',
+ meta: [
+ {
+ name: 'description',
+ content: 'This is an example Description Meta tag!'
+ }
+ ],
+ links: [
+ { rel: 'canonical', href: 'http://blogs.example.com/blah/nice' },
+ {
+ rel: 'alternate',
+ hreflang: 'es',
+ href: 'http://es.example.com/'
+ }
+ ]
+ }
+ },
+ {
+ path: 'counter',
+ component: CounterComponent,
+ data: {
+ title: 'Counter',
+ meta: [
+ {
+ name: 'description',
+ content: 'This is an Counter page Description!'
+ }
+ ],
+ links: [
+ {
+ rel: 'canonical',
+ href: 'http://blogs.example.com/counter/something'
+ },
+ {
+ rel: 'alternate',
+ hreflang: 'es',
+ href: 'http://es.example.com/counter'
+ }
+ ]
+ }
+ },
+ {
+ path: 'users',
+ component: UsersComponent,
+ data: {
+ title: 'Users REST example',
+ meta: [
+ {
+ name: 'description',
+ content: 'This is User REST API example page Description!'
+ }
+ ],
+ links: [
+ {
+ rel: 'canonical',
+ href: 'http://blogs.example.com/chat/something'
+ },
+ {
+ rel: 'alternate',
+ hreflang: 'es',
+ href: 'http://es.example.com/users'
+ }
+ ]
+ }
+ },
+ {
+ path: 'ngx-bootstrap',
+ component: NgxBootstrapComponent,
+ data: {
+ title: 'Ngx-bootstrap demo!!',
+ meta: [
+ {
+ name: 'description',
+ content: 'This is an Demo Bootstrap page Description!'
+ }
+ ],
+ links: [
+ {
+ rel: 'canonical',
+ href: 'http://blogs.example.com/bootstrap/something'
+ },
+ {
+ rel: 'alternate',
+ hreflang: 'es',
+ href: 'http://es.example.com/bootstrap-demo'
+ }
+ ]
+ }
+ },
+
+ {
+ path: 'lazy',
+ loadChildren: './containers/lazy/lazy.module#LazyModule'
+ },
+
+ {
+ path: '**',
+ component: NotFoundComponent,
+ data: {
+ title: '404 - Not found',
+ meta: [{ name: 'description', content: '404 - Error' }],
+ links: [
+ {
+ rel: 'canonical',
+ href: 'http://blogs.example.com/bootstrap/something'
+ },
+ {
+ rel: 'alternate',
+ hreflang: 'es',
+ href: 'http://es.example.com/bootstrap-demo'
+ }
+ ]
+ }
+ }
+ ],
+ {
+ // Router options
+ useHash: false,
+ preloadingStrategy: PreloadAllModules,
+ initialNavigation: 'enabled'
+ }
+ )
+ ],
+ providers: [LinkService, UserService, TranslateModule],
+ bootstrap: [AppComponent]
+})
+export class AppModuleShared {}
diff --git a/ClientApp/app/components/navmenu/navmenu.component.html b/ClientApp/app/components/navmenu/navmenu.component.html
new file mode 100644
index 00000000..ba9ff74e
--- /dev/null
+++ b/ClientApp/app/components/navmenu/navmenu.component.html
@@ -0,0 +1,41 @@
+
diff --git a/ClientApp/app/components/navmenu/navmenu.component.scss b/ClientApp/app/components/navmenu/navmenu.component.scss
new file mode 100644
index 00000000..66115049
--- /dev/null
+++ b/ClientApp/app/components/navmenu/navmenu.component.scss
@@ -0,0 +1,114 @@
+@import 'http://github.com/variables';
+// Mobile first styling.
+/* Apply for small displays */
+
+.navbar {
+ position: fixed;
+ background-color: theme-color('primary');
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+}
+
+.navbar,
+a,
+button {
+ color: $body-bg;
+}
+
+.nav {
+ padding: 0px;
+ background-color: white;
+}
+
+.nav-item a {
+ /* If a menu item's text is too long, truncate it */
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ padding-right: 5px;
+}
+
+.nav-link {
+ display: flex;
+ svg {
+ margin-right: 10px;
+ width: 20px;
+ height: 20px;
+ }
+}
+
+.navbar-brand {
+ font-size: 1.15rem;
+}
+
+li a {
+ border: 1px solid transparent;
+ &:hover {
+ border: 1px solid theme-color-level(primary, 2);
+ }
+}
+
+li.link-active a,
+li.link-active a:hover,
+li.link-active a:focus {
+ background-color: theme-color('accent');
+ color: $body-bg;
+}
+
+@include media-breakpoint-up(lg) {
+ .navbar {
+ width: 275px;
+ min-height: 1px;
+ padding-right: 15px;
+ padding-left: 15px;
+ background-color: $body-bg;
+ flex: 0 0 $menu-max-width;
+ min-width: $menu-max-width;
+ position: fixed;
+ display: flex;
+ flex-direction: column;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ padding: 0px;
+ align-items: flex-start;
+ box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1);
+ }
+ .navbar,
+ a,
+ button {
+ color: theme-color('primary');
+ }
+ .navbar-brand {
+ padding: 15px 15px;
+ font-size: 100%;
+ line-height: 20px;
+ height: 50px;
+ }
+ .navbar-collapse {
+ width: 100%;
+ flex: 1;
+ align-items: flex-start;
+ }
+ .nav {
+ width: 100%;
+ border-top: 1px solid #444;
+ background-color: transparent;
+ }
+ .nav-item {
+ float: none;
+ font-size: 15px;
+ margin: 6px;
+ }
+ .nav-item a {
+ padding: 10px 16px;
+ border-radius: 4px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+}
diff --git a/ClientApp/app/components/navmenu/navmenu.component.ts b/ClientApp/app/components/navmenu/navmenu.component.ts
new file mode 100644
index 00000000..d3dadb4d
--- /dev/null
+++ b/ClientApp/app/components/navmenu/navmenu.component.ts
@@ -0,0 +1,22 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-nav-menu',
+ templateUrl: './navmenu.component.html',
+ styleUrls: ['./navmenu.component.scss']
+})
+export class NavMenuComponent {
+ collapse: string = 'collapse';
+
+ collapseNavbar(): void {
+ if (this.collapse.length > 1) {
+ this.collapse = '';
+ } else {
+ this.collapse = 'collapse';
+ }
+ }
+
+ collapseMenu() {
+ this.collapse = 'collapse';
+ }
+}
diff --git a/ClientApp/app/components/user-detail/user-detail.component.html b/ClientApp/app/components/user-detail/user-detail.component.html
new file mode 100644
index 00000000..f43b4bbb
--- /dev/null
+++ b/ClientApp/app/components/user-detail/user-detail.component.html
@@ -0,0 +1,24 @@
+
+
+ Consulting | Development | Training | Workshops
+
+ Get your Team or Application up to speed by working with some of the leading industry experts in JavaScript, Node / NestJS, & ASP.NET!
+
+ Follow us on Twitter!
-### What is this repo? Live Demo here: [http://aspnetcore-angular2-universal.azurewebsites.net](http://aspnetcore-angular2-universal.azurewebsites.net)**
-This repository is maintained by [Angular Universal](https://github.com/angular/universal) and is meant to be an advanced starter for both ASP.NET Core using
-Angular 2.x(+), not only for the client-side, but to be rendered on the *server* for instant application paints
-(*Note*: If you don't need Universal (SSR) [read here](https://github.com/MarkPieszak/aspnetcore-angular2-universal#faq) on how to disable it).
+
+
+### What is this repo? Live Demo here: http://aspnetcore-angular2-universal.azurewebsites.net
-**How do I stay up-to-date with the latest updates & changes of the repo?** [Check out the CHANGELOG here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/CHANGELOG.md)
+This repository is maintained by [Trilon.io](https://Trilon.io) and the [Angular](https://github.com/angular/angular) Universal team and is meant to be an advanced starter
+for both ASP.NET Core 2.1 using Angular 7.0+, not only for the client-side, but to be rendered on the server for instant
+application paints (Note: If you don't need SSR [read here](#faq) on how to disable it).
This is meant to be a Feature-Rich Starter application containing all of the latest technologies, best build systems available, and include many real-world examples and libraries needed in todays Single Page Applications (SPAs).
This utilizes all the latest standards, no gulp, no bower, no typings, no manually "building" anything. NPM, Webpack and .NET handle everything for you!
-[1/23] Fully operational, some improvements & new functionality to come, keep an eye out for the latest updates!
-
-> NOTE: Angular Universal is currently moving to Angular Core (https://github.com/angular/angular/issues/13822), we will need to wait for that and angular v4 to finish certain aspects (aot for example)
+### Join us on Gitter
----
+[](https://gitter.im/aspnetcore-angular2-universal/)
# Table of Contents
@@ -29,10 +49,11 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual
* [Deployment](#deployment)
* [Upcoming Features](#upcoming-features)
* [Application Structure](#application-structure)
-* [Universal Gotchas](#universal-gotchas)
-* [FAQ](#faq)
+* [Gotchas](#gotchas)
+* [FAQ](#faq---also-check-out-the-faq-issues-label-and-the-how-to-issues-label)
* [Special Thanks](#special-thanks)
* [License](#license)
+* [Trilon - Consulting & Training](#trilon---angular--aspnet---consulting--training--development)
---
@@ -40,43 +61,62 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual
> These are just some of the features found in this starter!
-- **Angular 2.x+** : (Currently working with latest Angular `2.4.7`)
- - Featuring Server-side rendering (Angular Universal)
- - Faster paints, better SEO, deep-linking, etc
- - NgRx - Reactive Redux state management architecture
- - Built to work with the real-time Redux Devtools. [Get the Chrome extension here](https://github.com/zalmoxisus/redux-devtools-extension)
+- ASP.NET 2.1 - VS2017 support now!
+ - Azure delpoyment straight from VS2017
+ - Built in docker support through VS2017
+ - RestAPI (WebAPI) integration
+ - SQL Database CRUD demo
+ - Swagger WebAPI documentation when running in development mode
+ - SignalR Chat demo! (Thanks to [@hakonamatata](https://github.com/hakonamatata))
+
+- **Angular 7.0.0** :
+ - PWA (Progressive Web App)
+ - (Minimal) Angular-CLI integration
+ - This is to be used mainly for Generating Components/Services/etc.
+ - Usage examples:
+ - `ng g c components/example-component`
+ - `ng g s shared/some-service`
+ - Featuring Server-side rendering (Platform-Server, aka: "Universal")
+ - Faster initial paints, SEO (Search-engine optimization w Title/Meta/Link tags), social media link-previews, etc
+ - i18n internationalization support (via/ ngx-translate)
- Baked in best-practices (follows Angular style guide)
- - Bootstrap4 (with ng2-bootstrap) - can be rendered on the server
+ - Bootstrap3 (with ngx-bootstrap) - (can be rendered on the server!)
+ - Can be easily replaced with bootstrap4 (3 is provided for browser support)
+ - Bootstrap using SCSS / SASS for easy theming / styling!
-- **Webpack build system (Webpack 2)**
+- **Webpack build system (Webpack 4)**
- HMR : Hot Module Reloading/Replacement
- - NgRx utilized and setup to **hold app State between HMR builds**
- - Production builds
- - Webpack Dashboard
-
-- **Docker support**
+ - Production builds w/ AoT Compilation
- **Testing frameworks**
- - Unit testing with Karma/Jasmine
- - E2E testing with Protractor
+ - Unit testing with Jest (Going back to Karma soon)
- **Productivity**
- Typescript 2
- - Codelyzer (for Real-Sime static code analysis)
+ - Codelyzer (for Real-time static code analysis)
- VSCode & Atom provide real-time analysis out of the box.
- - **NOTE**: Does not fully work with Visual Studio yet. (Even with 1.1.0-preview)
-- **ASP.NET Core 1.1**
- - RestAPI integration
+- **ASP.NET Core 2.1**
+
- Integration with NodeJS to provide pre-rendering, as well as any other Node module asset you want to use.
- **Azure**
- Microsoft Application Insights setup (for MVC & Web API routing)
- - (Client-side Angular2 Application Insights integration coming soon)
+ - Client-side Angular Application Insights integration
+ - If you're using Azure simply install `npm i -S @markpieszak/ng-application-insights` as a dependencies.
+ - Note: Make sure only the Browser makes these calls ([usage info here](https://github.com/MarkPieszak/angular-application-insights/blob/master/README.md#usage))
+ - More information here: - https://github.com/MarkPieszak/angular-application-insights
+ ```typescript
+ // Add the Module to your imports
+ ApplicationInsightsModule.forRoot({
+ instrumentationKey: 'Your-Application-Insights-instrumentationKey'
+ })
+ ```
+- **Docker**
+ - Built in Visual Studio F5 Debugging support
+ - Uses the very light weight microsoft/dotnet image
+ - Currently limited to Linux image as there is a bug with running nodejs in a container on Windows.
-- **REST API CRUD demo with Entity Framework Core**
- - **[Postman](https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop) is used for testing the API**
- - [View examples here](./docs/RESTAPI-ENTITY.MD)
----
@@ -84,34 +124,47 @@ This utilizes all the latest standards, no gulp, no bower, no typings, no manual
# Getting Started?
-**Make sure you have at least Node 4.x or higher installed!**
+- **Make sure you have at least Node 8.11.1 or higher (w/ npm 5+) installed!**
+- **This repository uses ASP.Net Core 2.1, which has a hard requirement on .NET Core Runtime 2.1 and .NET Core SDK 2.1. Please install these items from [here](https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-net-core-2-1/?WT.mc_id=blog-twitter-timheuer)**
-**Development Mode**
-> You'll need ASP.NET Core installed (1.0.1 or 1.1).
-> Make sure you have VStudio 2015 update 3 installed as well.
+### Visual Studio 2017
- - Fork & Clone repo
- - `npm install && dotnet restore` (if using Visual Studio it will do both of these automatically when the project is opened)
-
- > Both Visual Studio & VSCode have the neccessary (Dev & Prod) Launch files to be able to run & debug immidiately.
-
-If you're **not** using VSCode (which sets it to Development mode when you push F5), make sure you set the `ASPNETCORE_ENVIRONMENT` variable to `Development`: (VSCode does this through the launch.json file)
-
-> Note: `set ASPNETCORE_ENVIRONMENT=Development` on Windows. `export ASPNETCORE_ENVIRONMENT=Development` on Mac/Linux.
+Make sure you have .NET Core 2.1 installed and/or VS2017 15.3.
+VS2017 will automatically install all the neccessary npm & .NET dependencies when you open the project.
+
+Simply push F5 to start debugging !
+
+**Docker-Support**: Change the startup project to docker-compose and press F5
----
+**Note**: If you get any errors after this such as `module not found: boot.server` (or similar), open up command line and run `npm run build:dev` to make sure all the assets have been properly built by Webpack.
-**Production Mode**
+### Visual Studio Code
-Run `dotnet publish`, when it finishes, `cd` into the directory where it put everything, typically it's within `/bin/Debug/netcoreapp1.1/publish/`.
+> Note: Make sure you have the C# extension & .NET Core Debugger installed.
-Now you can fire up the production applicatoin by running `dotnet aspnetcore-angular2-universal.dll` (If you renamed the project the part before `.dll` could be different, and your project name.
+The project comes with the configured Launch.json files to let you just push F5 to start the project.
-VSCode option 2: go to Command Line and set the environment variable
-to: `ASPNETCORE_ENVIRONMENT=Production`, then run `webpack` manually. Then you can launch the [Production] Launch Web option from VSCode.
+```bash
+# cd into the directory you cloned the project into
+npm install && npm run build:dev && dotnet restore
+# or yarn install
+```
-> Note: `set ASPNETCORE_ENVIRONMENT=Production` on Windows. `export ASPNETCORE_ENVIRONMENT=Production` on Mac/Linux.
+If you're running the project from command line with `dotnet run` make sure you set your environment variables to Development (otherwise things like HMR might not work).
+
+```bash
+# on Windows:
+set ASPNETCORE_ENVIRONMENT=Development
+# on Mac/Linux
+export ASPNETCORE_ENVIRONMENT=Development
+```
+
+# Upcoming Features:
+
+- Clean API / structure / simplify application
+- Refactor to latest RxJs pipeable syntax
+- Attempt to integrate with Angular-CLI fully
----
@@ -136,29 +189,6 @@ git remote add azure https://your-user-name@my-angular2-site.scm.azurewebsites.n
git push --set-upstream azure master
```
-----
-
-----
-
-# UPCOMING Features:
-
- - [ ] (On-hold) Example of NgRx (redux) transfering App State from server to client - track [#29](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/29)
- - [ ] (On-hold) AoT (Ahead-of-time compilation) production builds - track [#10](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/10)
- - [x] ~~SignalR (Websockets) example - track [#39](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/39)~~
- - [x] ~~Automatically update Browser Title on Route change [#32](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/32)~~
- - [x] ~~Update components real unit & e2e tests - track [#45](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/45)~~
- - [x] ~~Storage Service (localStorage) showcasing Dependency Injection per-platform - [#35](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues/35)~~
- - [x] ~~NgRx (reactive Redux application state management)~~
- - [x] ~~HMR State management (hold state when hot reload occurs)~~
- - [x] ~~Unit testing with Karma/Jasmine~~
- - [x] ~~Add e2e protractor tests~~
- - [x] ~~Add codelyzer for static code analysis (VSCode / Atom only so far)~~
- - [x] ~~Angular 2.1.1+ fixes (for Universal)~~
-
-----
-
-----
-
# Application Structure:
> Note: This application has WebAPI (our REST API) setup inside the same project, but of course all of this
@@ -178,182 +208,238 @@ Here we have the *usual suspects* found at the root level.
- `protractor` - config files (e2e testing)
- `tslint` - TypeScript code linting rules
-### **`/Client/**` - The entire Angular project is in here**
+### /ClientApp/ - Everything Angular
> Let's take a look at how this is structured so we can make some sense of it all!
-With Angular Universal, we need to split things **per platform** so [if we look inside this folder](https://github.com/MarkPieszak/aspnetcore-angular2-universal/tree/master/Client),
-you'll see the 2 most important files (also, these are how the entire application starts up):
+With Angular Universal, we need to split our applicatoin logic **per platform** so [if we look inside this folder](./ClientApp),
+you'll see the 2 root files, that branch the entire logic for browser & server respectively.
-- [**Bootstrap-Client.ts**](https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/bootstrap-client.ts) -
+- [**Boot.Browser.ts**](./ClientApp/boot.browser.ts) -
This file starts up the entire Angular application for the Client/browser platform.
-Here we setup a few things, client Angular bootstrapping, and also a little hack to handle
-Hot Module Replacement (HMR), and make sure we *hold* our NgRx (RxJs redux application Store) state
-between HMR cycles, ie: when you make a change to any Client file.
+Here we setup a few things, client Angular bootstrapping.
-You'll barely need to touch this file, but this is where you would import libraries that you only want
-being used in the Browser.
+You'll barely need to touch this file, but something to note, this is the file where you would import libraries that you **only** want
+being used in the Browser. (Just know that you'd have to provide a mock implementation for the Server when doing that).
-
-- [**Bootstrap-Server.ts**](https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/bootstrap-server.ts) -
-This file is where Angular Universal *serializes* the Angular application itself on the .NET server
+- [**Boot.Server.ts**](./ClientApp/boot.server.ts) -
+This file is where Angular _platform-server_ *serializes* the Angular application itself on the .NET server
within a very quick Node process, and renders it a string. This is what causes that initial fast paint
-of the entire application to the Browser.
+of the entire application to the Browser, and helps us get all our _SEO_ goodness :sparkles:
+
+---
-Notice the folder structure here in `./Client/` :
+Notice the folder structure here in `./ClientApp/` :
```diff
-+ /Client/
++ /ClientApp/
+ /app/
- ROOT App NgModule / Component / Routes / global css styles
+ App NgModule - our Root NgModule (you'll insert Components/etc here most often)
+ AppComponent / App Routes / global css styles
-++ > ++ > /platform-modules/
- Platform specific NgModules (browser & server separated)
-++ > ++ > /shared-module/
- BaseSharedModule (the lowest delimiter NgModule)
-++ > ++ > /state/
- Here you will find our NgRx "State", Reducers, and magic HMR handler
+ * Notice that we have 2 dividing NgModules:
+ app.module.browser & app.module.server
+ You'll almost always be using the common app.module, but these 2 are used to split up platform logic
+ for situations where you need to use Dependency Injection / etc, between platforms.
+Note: You could use whatever folder conventions you'd like, I prefer to split up things in terms of whether they are re-usable
+ "components" or routeable / page-like components that group together and organize entire sections.
++ > ++ > /components/
- Here are all the regular Components that aren't "Pages" or container Components
+ Here are all the regular Components that aren't "Pages" or container Components
-++ > /containers/
- These are the routeable or "Page / Container" Components, sometimes known as "Dumb" Components
+++ > ++ > /containers/
+ These are the routeable or "Page / Container" Components, sometimes known as "Dumb" Components
-++ > /shared/
- Here we put all shared Services / Directives / Pipes etc
+++ > ++ > /shared/
+ Here we put all shared Services / Directives / Pipes etc
```
When adding new features/components/etc to your application you'll be commonly adding things to the Root **NgModule** (located
-in `/Client/app/app.module.ts`), but why are there **two** other NgModules in `/platform-modules`?
+in `/ClientApp/app/app.module.ts`), but why are there **two** other NgModules in this folder?
-This is because we want to split our logic **per Platform**, but notice they both share a Common NgModule
-named `app.module.ts` (in the folder below them). When adding most things to your application, this is the only
+This is because we want to split our logic **per Platform**, but notice they both share the Common NgModule
+named `app.module.ts`. When adding most things to your application, this is the only
place you'll have to add in your new Component / Directive / Pipe / etc. You'll only occassional need to manually
-add in the Platform specific things to either `app.browser.module || app.server.module`.
+add in the Platform specific things to either `app.module.browser || app.module.server`.
-For example you can see how we're using Dependency Injection to inject a `StorageService` that is different
+To illustrate this point with an example, you can see how we're using Dependency Injection to inject a `StorageService` that is different
for the Browser & Server.
```typescript
-// For the Browser (app.browser.module)
+// For the Browser (app.module.browser)
{ provide: StorageService, useClass: BrowserStorage }
-// For the Server (app.server.module)
+// For the Server (app.module.server)
{ provide: StorageService, useClass: ServerStorage }
```
-Besides that you can see how each platform NgModule has the neccessary Universal lifecycle hooks to setup HttpCacheService, etc.
+> Just remember, you'll usually only need to worry about `app.module.ts`, as that's where you'll be adding most
+of your applications new aspects!
-> Just remember, you'll usually only need to worry about `app.common.module`, as that's where you'll be adding most
-of your applications new aspects.
-
-### **`/Server/**` - MVC & WebAPI Controllers**
+### /Server/ - Our REST API (WebApi) - MVC Controller
> As we pointed out, these are here for simplicities sake, and realistically you may want separate projects
for all your microservices / REST API projects / etc.
+We're utilizing MVC within this application, but we only need & have ONE Controller, named `HomeController`. This is where our entire
+Angular application gets serialized into a String, sent to the Browser, along with all the assets it needs to then bootstrap on the client-side, and become a full-blown SPA afterwards.
+
+---
+
+The short-version is that we invoke that Node process, passing in our Request object & invoke the `boot.server` file, and we get back a nice object that we pass into .NETs `ViewData` object, and sprinkle through out our `Views/Shared/_Layout.cshtml` and `/Views/Home/index.cshtml` files!
+
+A more detailed explanation can be found here: [ng-AspnetCore-Engine Readme](https://github.com/angular/universal/tree/master/modules/aspnetcore-engine)
+
+```csharp
+// Prerender / Serialize application
+var prerenderResult = await Prerenderer.RenderToString(
+ /* all of our parameters / options / boot.server file / customData object goes here */
+);
+
+ViewData["SpaHtml"] = prerenderResult.Html;
+ViewData["Title"] = prerenderResult.Globals["title"];
+ViewData["Styles"] = prerenderResult.Globals["styles"];
+ViewData["Meta"] = prerenderResult.Globals["meta"];
+ViewData["Links"] = prerenderResult.Globals["links"];
+
+return View(); // let's render the MVC View
+```
+
+Take a look at the `_Layout.cshtml` file for example, notice how we let .NET handle and inject all our SEO magic (that we extracted from Angular itself) !
+
+```html
+
+
+
+
+
+ @ViewData["Title"] - AspNET.Core Angular 7.0.0 (+) starter
+
+
+
+ @Html.Raw(ViewData["Meta"])
+ @Html.Raw(ViewData["Links"])
+
+
+
+ @Html.Raw(ViewData["Styles"])
+
+
+ ... etc ...
+```
+
+Our `Views/Home/index.cshtml` simply renders the application and serves the bundled webpack files in it.
+
+```html
+@Html.Raw(ViewData["SpaHtml"])
+
+
+@section scripts {
+
+}
+```
+
+### What happens after the App gets server rendered?
+
+Well now, your Client-side Angular will take over, and you'll have a fully functioning SPA. (But we gained all these great SEO benefits of being server-rendered) !
+
+:sparkles:
+
+
----
----
-# Universal "Gotchas"
+# "Gotchas"
-> When building "Universal" components in Angular 2 there are a few things to keep in mind.
+- This repository uses ASP.Net Core 2.1, which has a hard requirement on .NET Core Runtime 2.1 and .NET Core SDK 2.1. Please install these items from [here](https://blogs.msdn.microsoft.com/dotnet/2018/05/30/announcing-net-core-2-1/?WT.mc_id=blog-twitter-timheuer)
-**What does it mean to be Universal?**
+> When building components in Angular 7 there are a few things to keep in mind.
-It means that every Component in your application is going to be ran on the Server, *as well* as the Client (browser)!
-There are some important things to consider when creating an application like this,
-below we'll list out a few common problems developers trying to make isomorphic javascript applications run into:
+ - Make sure you provide Absolute URLs when calling any APIs. (The server can't understand relative paths, so `/api/whatever` will fail).
+
+ - API calls will be ran during a server, and once again during the client render, so make sure you're using transfering data that's important to you so that you don't see a flicker.
- **`window`**, **`document`**, **`navigator`**, and other browser types - _do not exist on the server_ - so using them, or any library that uses them (jQuery for example) will not work. You do have some options, if you truly need some of this functionality:
- - If you need to use them, consider limiting them to only your main.client and wrapping them situationally with the imported *isBrowser / isNode* features from Universal. `import { isBrowser, isNode } from 'angular2-universal'`;
- - Another option is using `DOM` from ["@angular/platform-browser"](https://github.com/angular/angular/blob/e3687706c71beb7c9dbdae1bbb5fbbcea588c476/modules/%40angular/platform-browser/src/dom/dom_adapter.ts#L34)
- - To use `templateUrl` or `styleUrls` you must use **`angular2-template-loader`** in your TypeScript loaders.
- - This is already setup within this starter repo. Look at the webpack.config file
- [here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/webpack.config.js#L59-L67) for details & implementation.
- - Try to *limit or* **avoid** using **`setTimeout`**. It will slow down the server-side rendering process. Make sure to remove them [`ondestroy`](https://angular.io/docs/ts/latest/api/core/index/OnDestroy-class.html) in Components.
+ - If you need to use them, consider limiting them to only your client and wrapping them situationally. You can use the Object injected using the PLATFORM_ID token to check whether the current platform is browser or server.
+
+ ```typescript
+ import { PLATFORM_ID } from '@angular/core';
+ import { isPlatformBrowser, isPlatformServer } from '@angular/common';
+
+ constructor(@Inject(PLATFORM_ID) private platformId: Object) { ... }
+
+ ngOnInit() {
+ if (isPlatformBrowser(this.platformId)) {
+ // Client only code.
+ ...
+ }
+ if (isPlatformServer(this.platformId)) {
+ // Server only code.
+ ...
+ }
+ }
+ ```
+
+ - Try to *limit or* **avoid** using **`setTimeout`**. It will slow down the server-side rendering process. Make sure to remove them [`ngOnDestroy`](https://angular.io/docs/ts/latest/api/core/index/OnDestroy-class.html) in Components.
- Also for RxJs timeouts, make sure to _cancel_ their stream on success, for they can slow down rendering as well.
- - **Don't manipulate the nativeElement directly**. Use the _Renderer_. We do this to ensure that in any environment we're able to change our view.
+ - **Don't manipulate the nativeElement directly**. Use the _Renderer2_. We do this to ensure that in any environment we're able to change our view.
```typescript
-constructor(element: ElementRef, renderer: Renderer) {
- renderer.setElementStyle(element.nativeElement, 'font-size', 'x-large');
+constructor(element: ElementRef, renderer: Renderer2) {
+ this.renderer.setStyle(element.nativeElement, 'font-size', 'x-large');
}
```
- The application runs XHR requests on the server & once again on the Client-side (when the application bootstraps)
- - Use a Universal [HttpCacheService](https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/shared/cache/api.ts#L14-L47) instead of making regular Http requests, to save certain requests so they aren't re-ran again on the Client. [demo'd here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/containers/rest-test/rest-test.component.ts#L32-L40)
+ - Use a cache that's transferred from server to client (TODO: Point to the example)
- Know the difference between attributes and properties in relation to the DOM.
- - Keep your directives stateless as much as possible. For stateful directives, you may need to provide an attribute that reflects the corresponding property with an initial string value such as url in img tag. For our native `` element the src attribute is reflected as the _src_ property of the element type `HTMLImageElement`.
+ - Keep your directives stateless as much as possible. For stateful directives, you may need to provide an attribute that reflects the corresponding property with an initial string value such as url in img tag. For our native element the src attribute is reflected as the src property of the element type HTMLImageElement.
+ - Error: `sass-loader` requires `node-sass` >=4: Either in the docker container or localhost run npm rebuild node-sass -f
----
-
-# Found a Bug? Want to Contribute?
-
-Nothing's ever perfect, but please let me know by creating an issue (make sure there isn't an existing one about it already), and we'll try and work out a fix for it! If you have any good ideas, or want to contribute, feel free to either make an Issue with the Proposal, or just make a PR from your Fork.
----
-# FAQ
-
-### How to add a new Route
-
-1. Create a folder & your new Compoent inside: `Client\Containers\`
-2. Add the Component to: `Client\Containers\index.ts` (this is the "barrels" file for all containers)
-3. Add the Component to MODULES & ROUTES within: `\Client\app\platform-modules\app.common.module.ts`
-
-### How can I disable Universal / SSR (Server-side rendering)?
-
-To disable SSR, go to `Views/Home/Index.cshtml` and remove `asp-prerender-module="Client/bootstrap-server"` from the `` component there in the cshtml file.
+# FAQ - Also check out the [!FAQ Issues label!](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues?utf8=%E2%9C%93&q=is%3Aissue%20label%3Afaq) and the [!HOW-TO Issues Label!](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues?q=is%3Aissue+label%3A%22HOW+TO+-+Guide%22)
-### How do I prevent XHR calls from running again on the Client?
+### How can I disable SSR (Server-side rendering)?
-Using the provided GET from [HttpCacheService](https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/Client/shared/cache/api.ts#L14-L47) as opposed
-to regular `Http`, it will automatically Cache the response on the server, and pass it down through the html *for you*, and when the
-Client tries to run it again, it will instantly grab the result from there.
+Simply comment out the logic within HomeController, and replace `@Html.Raw(ViewData["SpaHtml"])` with just your applications root
+AppComponent tag ("app-root" in our case): ``.
-**This is essential for important XHR calls on a page that happen on page load**
+> You could also remove any `isPlatformBrowser/etc` logic, and delete the boot.server, app.module.browser & app.module.server files, just make sure your `boot.browser` file points to `app.module`.
### How do I have code run only in the Browser?
-Angular Universal has `isBrowser` & `isNode` you can import from `angular2-universal` to
-conditionally run code. This is perfect for situations where code could *error* on the server.
-Also, always remember that things like setTimeout / setInterval / etc should always be wrapped in this,
-as you want to completely *avoid* doing them on the Server.
+Check the [Gotchas](#gotchas) on how to use `isPlatformBrowser()`.
-```typescript
-import { isBrowser } from 'angular2-universal';
-
-if (isBrowser) {
- // do something only in the Browser
-}
-```
+### How do I Material2 with this repo?
-### How can I use Material2 with this Repo?
+~~You'll either want to remove SSR for now, or wait as support should be coming to handle platform-server rendering.~~
+This is now possible, with the recently updated Angular Material changes. We do not have a tutorial available for this yet.
-For now, Material2 is still in beta, and isn't fully functioning with Universal (it will sometime soon though!), so one option is to temporarily disable SSR (server-side rendering) so you can use it within your application until updates come from Material, and you can have it rendered on the server as well. Read the Material docs to see how to add Material to your Angular application, with SSR disabled everything should work without any problems.
-
-Another option is to look at the workaround [here](https://github.com/angular/material2/issues/308#issuecomment-275361791) and make a separate workaround file `__2.1.1.workaround.node.ts` that you import specifically to the `bootstrap-server.ts` file.
-
-### How can I use jQuery and/or some jQuery plugins with Angular Universal?
+### How can I use jQuery and/or some jQuery plugins with this repo?
> Note: If at all possible, try to avoid using jQuery or libraries dependent on it, as there are
-better, more abstract ways of dealing with the DOM in Angular (2+) such as using the Renderer, etc.
+better, more abstract ways of dealing with the DOM in Angular (5+) such as using the Renderer, etc.
Yes, of course but there are a few things you need to setup before doing this. First, make sure jQuery
is included in webpack vendor file, and that you have a webpack Plugin setup for it. `new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' })`
-Now, make sure any "plugins" etc that you have, are only included in your `bootstrap-client.ts` file. (ie: `import 'slick-carousel';`)
+Now, make sure any "plugins" etc that you have, are only included in your `boot.browser.ts` file. (ie: `import 'slick-carousel';`)
In a Component you want to use jQuery, make sure to import it near the top like so:
```typescript
import * as $ from 'jquery';
```
-**Always make sure to wrap anything jQuery oriented in Universal's `isBrowser` conditional!**
+**Always make sure to wrap anything jQuery oriented in Angular's `isPlatformBrowser()` conditional!**
+
+### How can I support IE9 through IE11?
+
+To support IE9 through IE11 open the `polyfills.ts` file in the `polyfills` folder and uncomment out the 'import polyfills' as needed. ALSO - make sure that your `webpack.config` and `webpack.config.vendor` change option of `TerserPlugin` from `ecma: 6` to **`ecma: 5`**.
----
@@ -361,6 +447,22 @@ import * as $ from 'jquery';
Many thanks go out to Steve Sanderson ([@SteveSandersonMS](https://github.com/SteveSandersonMS)) from Microsoft and his amazing work on JavaScriptServices and integrating the world of Node with ASP.NET Core.
+Also thank you to the many Contributors !
+- [@GRIMMR3AP3R](https://github.com/GRIMMR3AP3R)
+- [@Isaac2004](https://github.com/Isaac2004)
+- [@AbrarJahin](https://github.com/AbrarJahin)
+- [@LiverpoolOwen](https://github.com/LiverpoolOwen)
+- [@hakonamatata](https://github.com/hakonamatata)
+- [@markwhitfeld](https://github.com/markwhitfeld)
+- [@Ketrex](https://github.com/Ketrex)
+
+----
+
+# Found a Bug? Want to Contribute?
+
+[Check out our easier issues here](https://github.com/MarkPieszak/aspnetcore-angular2-universal/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3A%22PRs%20welcome!%22%20)
+
+Nothing's ever perfect, but please let me know by creating an issue (make sure there isn't an existing one about it already), and we'll try and work out a fix for it! If you have any good ideas, or want to contribute, feel free to either make an Issue with the Proposal, or just make a PR from your Fork.
----
@@ -368,6 +470,24 @@ Many thanks go out to Steve Sanderson ([@SteveSandersonMS](https://github.com/St
[](/LICENSE)
-Copyright (c) 2016-2017 [Mark Pieszak](https://github.com/MarkPieszak)
+Copyright (c) 2016-2019 [Mark Pieszak](https://github.com/MarkPieszak)
+
+[](https://twitter.com/MarkPieszak)
+
+----
+
+# Trilon - Angular & ASP.NET - Consulting | Training | Development
+
+Check out **[Trilon.io](https://Trilon.io)** for more info! Twitter [@Trilon_io](http://www.twitter.com/Trilon_io)
+
+Contact us at , and let's talk about your projects needs.
+
+
+
+## Follow Trilon online:
-Twitter: [@MarkPieszak](http://twitter.com/MarkPieszak) | Medium: [@MarkPieszak](https://medium.com/@MarkPieszak)
+Twitter: [@Trilon_io](http://twitter.com/Trilon_io)
diff --git a/Server/Api/TestController.cs b/Server/Api/TestController.cs
deleted file mode 100644
index c1bb8437..00000000
--- a/Server/Api/TestController.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using Angular2Spa.Hubs;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.SignalR.Infrastructure;
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.AspNetCore.SignalR;
-using System.IO;
-
-namespace Angular2Spa.Controllers
-{
- [Route("api/[controller]")]
- public class TestController : Controller
- {
- /*
- private IHubContext _hub;
- private ILogger _logger;
- private string uploadDirectory;
-
- public TestController(ILogger logger, IHostingEnvironment environment, IConnectionManager connectionManager) //, IHostingEnvironment environment
- {
- _hub = connectionManager.GetHubContext();
- _logger = logger;
- IHostingEnvironment _environment = environment;
- var location = System.Reflection.Assembly.GetEntryAssembly().Location;
- uploadDirectory = _environment.WebRootPath + $@"/{"uploads"}";
- Directory.CreateDirectory(uploadDirectory); //Should be in startup
- }
- */
-
- private static string[] Names = new[]
- {
- "Mark Pieszak", "Angular mcAngular", "Redux-man", "Nintendo"
- };
-
- [HttpGet("[action]")]
- public IEnumerable Users()
- {
- var random = new Random();
-
- //Calling a hub function
- //_hub.Clients.All.Send("REST Working");
-
- return Enumerable.Range(1, 5).Select(index => new SampleData
- {
- ID = random.Next(0, 2000),
- Name = Names[random.Next(Names.Length)]
- });
- }
-
- public class SampleData
- {
- public int ID { get; set; }
- public string Name { get; set; }
- }
- }
-}
diff --git a/Server/Api/UserController.cs b/Server/Api/UserController.cs
deleted file mode 100644
index 2bcb5a13..00000000
--- a/Server/Api/UserController.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using Angular2Spa.Models;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.EntityFrameworkCore;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace Angular2Spa.Controllers
-{
- [Route("api/user/[action]")]
- public class UserController : Controller
- {
- private readonly SpaDbContext _context;
-
- public UserController(SpaDbContext context)
- {
- _context = context;
- }
-
- [HttpGet]
- public async Task All(int currentPageNo = 1, int pageSize = 20)
- {
- var users = await _context.User
- .OrderByDescending(u => u.EntryTime)
- .Skip((currentPageNo - 1) * pageSize)
- .Take(pageSize)
- .ToArrayAsync();
-
- return Ok(users);
- }
-
- [HttpGet]
- public async Task Details(int? id)
- {
- var user = await _context.User
- .Where(u => u.ID == id)
- .AsNoTracking()
- .SingleOrDefaultAsync(m => m.ID == id);
-
- if (user == null)
- {
- return NotFound("User not Found");
- }
- else
- {
- return Ok(user);
- }
- }
-
- [HttpPost]
- public async Task Insert( [Bind("Name")] Users user)
- {
- if(user.Name!=null)
- {
- _context.Add(user);
- await _context.SaveChangesAsync();
- return Ok(user);
- }
- else
- {
- return NotFound("Name not given");
- }
- }
-
- [HttpPut]
- public async Task Update([Bind("ID,Name")] Users userUpdateValue)
- {
- try
- {
- userUpdateValue.EntryTime = DateTime.Now;
- _context.Update(userUpdateValue);
- await _context.SaveChangesAsync();
- return Ok(userUpdateValue);
- }
- catch (DbUpdateException)
- {
- //Log the error (uncomment ex variable name and write a log.)
- ModelState.AddModelError("", "Unable to save changes. " +
- "Try again, and if the problem persists, " +
- "see your system administrator.");
- return NotFound("User not Found");
- }
- }
-
- [HttpDelete]
- public async Task Delete(int? id)
- {
- var userToRemove = await _context.User
- .AsNoTracking()
- .SingleOrDefaultAsync(m => m.ID == id);
- if (userToRemove == null)
- {
- return NotFound("User not Found");
- }
- else
- {
- _context.User.Remove(userToRemove);
- await _context.SaveChangesAsync();
- return Ok("Deleted user - "+ userToRemove.Name);
- }
- }
- }
-}
diff --git a/Server/Controllers/HomeController.cs b/Server/Controllers/HomeController.cs
index f2a40fa9..18c423c6 100644
--- a/Server/Controllers/HomeController.cs
+++ b/Server/Controllers/HomeController.cs
@@ -1,52 +1,47 @@
+using Asp2017.Server.Helpers;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http.Features;
-using Microsoft.AspNetCore.Mvc;
-
-using Microsoft.AspNetCore.SpaServices.Prerendering;
-using Microsoft.AspNetCore.NodeServices;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.AspNetCore.Hosting;
-
-namespace Angular2Spa.Controllers
-{
- public class HomeController : Controller
- {
- // public async Task Index()
- public IActionResult Index()
- {
- // var nodeServices = Request.HttpContext.RequestServices.GetRequiredService();
- // var hostEnv = Request.HttpContext.RequestServices.GetRequiredService();
-
- // var applicationBasePath = hostEnv.ContentRootPath;
- // var requestFeature = Request.HttpContext.Features.Get();
- // var unencodedPathAndQuery = requestFeature.RawTarget;
- // var unencodedAbsoluteUrl = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";
-
- // // Prerender / Serialize application (with Universal)
- // var prerenderResult = await Prerenderer.RenderToString(
- // "/",
- // nodeServices,
- // new JavaScriptModuleExport("Client/bootstrap-server"),
- // unencodedAbsoluteUrl,
- // unencodedPathAndQuery,
- // null,
- // 30000,
- // Request.PathBase.ToString()
- // );
-
- // ViewData["SpaHtml"] = prerenderResult.Html;
- // ViewData["Title"] = prerenderResult.Globals["pageTitle"];
-
- return View();
+namespace AspCoreServer.Controllers
+{
+ public class HomeController : Controller {
+ protected readonly IHostingEnvironment HostingEnvironment;
+ public HomeController(IHostingEnvironment hostingEnv) => this.HostingEnvironment = hostingEnv;
+
+ [HttpGet]
+ public async Task Index () {
+ var prerenderResult = await this.Request.BuildPrerender ();
+
+ this.ViewData["SpaHtml"] = prerenderResult.Html; // our from Angular
+ this.ViewData["Title"] = prerenderResult.Globals["title"]; // set our from Angular
+ this.ViewData["Styles"] = prerenderResult.Globals["styles"]; // put styles in the correct place
+ this.ViewData["Scripts"] = prerenderResult.Globals["scripts"]; // scripts (that were in our header)
+ this.ViewData["Meta"] = prerenderResult.Globals["meta"]; // set our SEO tags
+ this.ViewData["Links"] = prerenderResult.Globals["links"]; // set our etc SEO tags
+ this.ViewData["TransferData"] = prerenderResult.Globals["transferData"]; // our transfer data set to window.TRANSFER_CACHE = {};
+ if (!this.HostingEnvironment.IsDevelopment ()) {
+ this.ViewData["ServiceWorker"] = "";
+ }
+
+ return View ();
}
- public IActionResult Error()
- {
- return View();
- }
+ [HttpGet]
+ [Route("sitemap.xml")]
+ public IActionResult SitemapXml() => Content($@"
+
+
+ http://localhost:4251/home
+ { DateTime.Now.ToString("yyyy-MM-dd")}
+
+
+ http://localhost:4251/counter
+ {DateTime.Now.ToString("yyyy-MM-dd")}
+
+ ", "text/xml");
+
+ public IActionResult Error() => View();
}
}
diff --git a/Server/Data/CoreEFStartup.cs b/Server/Data/CoreEFStartup.cs
new file mode 100644
index 00000000..d3947ef1
--- /dev/null
+++ b/Server/Data/CoreEFStartup.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AspCoreServer.Data
+{
+ public static class CoreEFStartup
+ {
+ public static async Task InitializeDatabaseAsync(IServiceProvider services)
+ {
+ var context = services.GetRequiredService();
+
+ await context.Database.EnsureCreatedAsync();
+ }
+
+ }
+}
diff --git a/Server/Data/LoggingEFStartup.cs b/Server/Data/LoggingEFStartup.cs
new file mode 100644
index 00000000..edf33498
--- /dev/null
+++ b/Server/Data/LoggingEFStartup.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Threading.Tasks;
+
+namespace AspCoreServer.Data
+{
+ public static class LoggingEFStartup
+ {
+ public static async Task InitializeDatabaseAsync(IServiceProvider services)
+ {
+ //Implent to your hearts' content
+ }
+ }
+}
diff --git a/Server/Data/SimpleContentEFStartup.cs b/Server/Data/SimpleContentEFStartup.cs
new file mode 100644
index 00000000..db160dee
--- /dev/null
+++ b/Server/Data/SimpleContentEFStartup.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Threading.Tasks;
+using AspCoreServer.Models;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace AspCoreServer.Data
+{
+ public static class SimpleContentEFStartup
+ {
+ public static async Task InitializeDatabaseAsync(IServiceProvider services)
+ {
+ var context = services.GetRequiredService();
+
+
+ if (await context.User.AnyAsync())
+ {
+ return; // DB has been seeded
+ }
+ var users = new User[] {
+ new User () { Name = "Mark Pieszak" },
+ new User () { Name = "Abrar Jahin" },
+ new User () { Name = "hakonamatata" },
+ new User () { Name = "LiverpoolOwen" },
+ new User () { Name = "Ketrex" },
+ new User () { Name = "markwhitfeld" },
+ new User () { Name = "daveo1001" },
+ new User () { Name = "paonath" },
+ new User () { Name = "nalex095" },
+ new User () { Name = "ORuban" },
+ new User () { Name = "Gaulomatic" },
+ new User () { Name = "GRIMMR3AP3R" }
+ };
+ await context.User.AddRangeAsync(users);
+
+ await context.SaveChangesAsync();
+ }
+ }
+}
diff --git a/Server/Data/SpaDbContext.cs b/Server/Data/SpaDbContext.cs
new file mode 100644
index 00000000..0f90c230
--- /dev/null
+++ b/Server/Data/SpaDbContext.cs
@@ -0,0 +1,13 @@
+using AspCoreServer.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace AspCoreServer.Data {
+ public class SpaDbContext : DbContext {
+ public SpaDbContext (DbContextOptions options) : base (options) {
+ Database.EnsureCreated ();
+ }
+
+ //List of DB Models - Add your DB models here
+ public DbSet User { get; set; }
+ }
+}
diff --git a/Server/Helpers/HttpRequestExtensions.cs b/Server/Helpers/HttpRequestExtensions.cs
new file mode 100644
index 00000000..81c613b9
--- /dev/null
+++ b/Server/Helpers/HttpRequestExtensions.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Asp2017.Server.Models;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.NodeServices;
+using Microsoft.AspNetCore.SpaServices.Prerendering;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Asp2017.Server.Helpers {
+ public static class HttpRequestExtensions {
+ public static IRequest AbstractRequestInfo(this HttpRequest request) => new IRequest()
+ {
+ cookies = request.Cookies,
+ headers = request.Headers,
+ host = request.Host
+ };
+
+ public static async Task BuildPrerender(this HttpRequest request) =>
+ // Prerender / Serialize application (with Universal)
+ await Prerenderer.RenderToString(
+ "/",
+ request.HttpContext.RequestServices.GetRequiredService(),
+ new System.Threading.CancellationTokenSource().Token,
+ new JavaScriptModuleExport(request.HttpContext.RequestServices.GetRequiredService().ContentRootPath + "/ClientApp/dist/main-server"),
+ $"{request.Scheme}://{request.Host}{request.HttpContext.Features.Get().RawTarget}",
+ request.HttpContext.Features.Get().RawTarget,
+ // ** TransferData concept **
+ // Here we can pass any Custom Data we want !
+ // By default we're passing down Cookies, Headers, Host from the Request object here
+ new TransferData
+ {
+ request = request.AbstractRequestInfo(),
+ thisCameFromDotNET = "Hi Angular it's asp.net :)"
+ }, // Our simplified Request object & any other CustommData you want to send!
+ 30000,
+ request.PathBase.ToString()
+ );
+ }
+}
diff --git a/Server/Hubs/ChatHub.cs b/Server/Hubs/ChatHub.cs
deleted file mode 100644
index e2528477..00000000
--- a/Server/Hubs/ChatHub.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Microsoft.AspNetCore.SignalR;
-using Microsoft.AspNetCore.SignalR.Hubs;
-using Microsoft.Extensions.Logging;
-using System.Threading.Tasks;
-
-namespace Angular2Spa.Hubs
-{
- [HubName("chat")]
- public class ChatHub : Hub
- {
- private readonly ILogger _logger;
- public static readonly string testGroupName = "test_group";
-
- public ChatHub(ILogger logger)
- {
- _logger = logger;
- }
-
- public override Task OnConnected()
- {
- _logger.LogWarning(
- Context.ConnectionId + " - Connected"
- );
- return base.OnConnected();
- }
-
-
- //rejoin groups if client disconnects and then reconnects
- public override Task OnReconnected()
- {
- _logger.LogWarning(
- Context.ConnectionId + " ReConnected"
- );
- return base.OnReconnected();
- }
-
- public override Task OnDisconnected(bool stopCalled)
- {
- _logger.LogWarning(
- Context.ConnectionId + " DisConnected"
- );
-
- return base.OnDisconnected(stopCalled);
- }
-
- public Task JoinGroup(string groupName)
- {
- return Groups.Add(Context.ConnectionId, groupName);
- }
-
- public Task LeaveGroup(string groupName)
- {
- return Groups.Remove(Context.ConnectionId, groupName);
- }
-
- public void Send(string message)
- {
- Clients.All.messageReceived(Context.ConnectionId.ToString(), message );
-
- _logger.LogWarning(
- Context.ConnectionId + " - " + message
- );
- }
- }
-}
diff --git a/Server/Models/IRequest.cs b/Server/Models/IRequest.cs
new file mode 100644
index 00000000..295c88fb
--- /dev/null
+++ b/Server/Models/IRequest.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Asp2017.Server.Models {
+ public class IRequest {
+ public object cookies { get; set; }
+ public object headers { get; set; }
+ public object host { get; set; }
+ }
+}
diff --git a/Server/Models/TransferData.cs b/Server/Models/TransferData.cs
new file mode 100644
index 00000000..c144d2df
--- /dev/null
+++ b/Server/Models/TransferData.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Asp2017.Server.Models {
+ public class TransferData {
+ public dynamic request { get; set; }
+
+ // Your data here ?
+ public object thisCameFromDotNET { get; set; }
+ }
+}
diff --git a/Server/Models/Users.cs b/Server/Models/User.cs
similarity index 56%
rename from Server/Models/Users.cs
rename to Server/Models/User.cs
index e59ab641..56e388bf 100644
--- a/Server/Models/Users.cs
+++ b/Server/Models/User.cs
@@ -1,20 +1,17 @@
using System;
using System.ComponentModel.DataAnnotations;
-namespace Angular2Spa.Models
-{
- public class Users
- {
+namespace AspCoreServer.Models {
+ public class User {
public int ID { get; set; }
public string Name { get; set; }
- [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
- [DataType(DataType.Date)]
+ [DisplayFormat (DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
+ [DataType (DataType.Date)]
public DateTime EntryTime { get; set; }
//Setting Default value
- public Users()
- {
+ public User () {
EntryTime = DateTime.Now;
}
}
diff --git a/Server/RestAPI/UsersController.cs b/Server/RestAPI/UsersController.cs
new file mode 100644
index 00000000..a23301e0
--- /dev/null
+++ b/Server/RestAPI/UsersController.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using AspCoreServer.Data;
+using AspCoreServer.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace AspCoreServer.Controllers {
+ [Route ("api/[controller]")]
+ public class UsersController : Controller {
+ private readonly SpaDbContext context;
+
+ public UsersController(SpaDbContext context) => this.context = context;
+
+ [HttpGet]
+ public async Task Get (int currentPageNo = 1, int pageSize = 20) {
+ var users = await this.context.User
+ .OrderByDescending (u => u.EntryTime)
+ .Skip ((currentPageNo - 1) * pageSize)
+ .Take (pageSize)
+ .ToArrayAsync ();
+
+ if (!users.Any ()) {
+ return NotFound ("Users not Found");
+ } else {
+ return Ok (users);
+ }
+ }
+
+ [HttpGet ("{id}")]
+ public async Task Get (int id) {
+ var user = await this.context.User
+ .Where (u => u.ID == id)
+ .AsNoTracking ()
+ .SingleOrDefaultAsync (m => m.ID == id);
+
+ if (user == null) {
+ return NotFound ("User not Found");
+ } else {
+ return Ok (user);
+ }
+ }
+
+ [HttpPost]
+ public async Task Post ([FromBody] User user) {
+ if (!string.IsNullOrEmpty (user.Name)) {
+ this.context.Add (user);
+ await this.context.SaveChangesAsync ();
+ return CreatedAtAction ("Post", user);
+ } else {
+ return BadRequest ("User's name was not given");
+ }
+ }
+
+ [HttpPut ("{id}")]
+ public async Task Put (int id, [FromBody] User userUpdateValue) {
+ try {
+ userUpdateValue.EntryTime = DateTime.Now;
+
+ var userToEdit = await context.User
+ .AsNoTracking ()
+ .SingleOrDefaultAsync (m => m.ID == id);
+
+ if (userToEdit == null) {
+ return NotFound ("Could not update user as it was not Found");
+ } else {
+ this.context.Update (userUpdateValue);
+ await this.context.SaveChangesAsync ();
+ return Json ("Updated user - " + userUpdateValue.Name);
+ }
+ } catch (DbUpdateException) {
+ //Log the error (uncomment ex variable name and write a log.)
+ this.ModelState.AddModelError ("", "Unable to save changes. " +
+ "Try again, and if the problem persists, " +
+ "see your system administrator.");
+ return NotFound ("User not Found");
+ }
+ }
+
+ [HttpDelete ("{id}")]
+ public async Task Delete (int id) {
+ var userToRemove = await this.context.User
+ .AsNoTracking ()
+ .SingleOrDefaultAsync (m => m.ID == id);
+ if (userToRemove == null) {
+ return NotFound ("Could not delete user as it was not Found");
+ } else {
+ this.context.User.Remove (userToRemove);
+ await this.context.SaveChangesAsync ();
+ return Json ("Deleted user - " + userToRemove.Name);
+ }
+ }
+ }
+}
diff --git a/Server/SpaDbContext.cs b/Server/SpaDbContext.cs
deleted file mode 100644
index a22a571b..00000000
--- a/Server/SpaDbContext.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Angular2Spa.Models;
-using Microsoft.EntityFrameworkCore;
-
-namespace Angular2Spa
-{
- public class SpaDbContext : DbContext
- {
- public SpaDbContext(DbContextOptions options)
- : base(options)
- {
- Database.EnsureCreated();
- //context.Database.Migrate();
- }
-
- //List of DB Models - Add your DB models here
- public DbSet User { get; set; }
- }
-}
diff --git a/Startup.cs b/Startup.cs
index 9993ac4a..8d978fd4 100644
--- a/Startup.cs
+++ b/Startup.cs
@@ -1,139 +1,109 @@
-using System;
+using System;
+using System.IO;
+using AspCoreServer.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Antiforgery;
-
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SpaServices.Webpack;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-using Microsoft.AspNetCore.Authentication.Cookies;
-using Microsoft.EntityFrameworkCore;
-using Angular2Spa.Models;
-using Microsoft.AspNetCore.Http;
-
-using Microsoft.AspNetCore.NodeServices;
-
-namespace Angular2Spa
-{
- public class Startup
- {
- public Startup(IHostingEnvironment env)
- {
- var builder = new ConfigurationBuilder()
- .SetBasePath(env.ContentRootPath)
- .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
- .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
- .AddEnvironmentVariables();
-
- if (env.IsDevelopment())
- {
- // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
- builder.AddApplicationInsightsSettings(developerMode: true);
- }
-
- Configuration = builder.Build();
+using Microsoft.Net.Http.Headers;
+using Swashbuckle.AspNetCore.Swagger;
+using WebEssentials.AspNetCore.Pwa;
+
+namespace AspCoreServer {
+ public class Startup {
+ public Startup (IHostingEnvironment env) {
+ var builder = new ConfigurationBuilder ()
+ .SetBasePath (env.ContentRootPath)
+ .AddJsonFile ("appsettings.json", optional : true, reloadOnChange : true)
+ .AddJsonFile ($"appsettings.{env.EnvironmentName}.json", optional : true)
+ .AddEnvironmentVariables ();
+ this.Configuration = builder.Build ();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- //Monitor for performance and usage
- services.AddApplicationInsightsTelemetry(Configuration);
+ public void ConfigureServices (IServiceCollection services) {
+ // Add framework services.
+ services.AddMvc ();
+ services.AddNodeServices ();
+ services.AddHttpContextAccessor ();
+ services.AddProgressiveWebApp (new PwaOptions { Strategy = ServiceWorkerStrategy.CacheFirst, RegisterServiceWorker = true, RegisterWebmanifest = true }, "manifest.json");
- //Add DB Context
var connectionStringBuilder = new Microsoft.Data.Sqlite.SqliteConnectionStringBuilder { DataSource = "spa.db" };
var connectionString = connectionStringBuilder.ToString();
- services.AddDbContext(options =>
- options.UseSqlite(connectionString));
-
- // Add framework services.
- services.AddMvc();
- services.AddMemoryCache();
-
- services.AddNodeServices();
- services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
+ services.AddDbContext(options =>
+ options.UseSqlite(connectionString));
- //Adding SignalR Service
- services.AddSignalR(options => {
- services.AddMemoryCache();
- var transports = options.Transports;
- options.Hubs.EnableDetailedErrors = true;
- transports.DisconnectTimeout = TimeSpan.FromSeconds(6);
- transports.KeepAlive = TimeSpan.FromSeconds(2);
- transports.TransportConnectTimeout = TimeSpan.FromSeconds(20);
+ // Register the Swagger generator, defining one or more Swagger documents
+ services.AddSwaggerGen (c => {
+ c.SwaggerDoc ("v1", new Info { Title = "Angular 7.0 Universal & ASP.NET Core advanced starter-kit web API", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IAntiforgery antiforgery)
- {
- loggerFactory.AddConsole(Configuration.GetSection("Logging"));
- loggerFactory.AddDebug();
-
- //SignalR Config
- app.UseSignalR();
-
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- //Adding Browser Link support for error catching live
- app.UseBrowserLink();
+ public void Configure (IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, SpaDbContext context) {
+ loggerFactory.AddConsole (this.Configuration.GetSection ("Logging"));
+ loggerFactory.AddDebug ();
+
+ // app.UseStaticFiles();
+
+ app.UseStaticFiles (new StaticFileOptions () {
+ OnPrepareResponse = c => {
+ //Do not add cache to json files. We need to have new versions when we add new translations.
+ c.Context.Response.GetTypedHeaders ().CacheControl = !c.Context.Request.Path.Value.Contains (".json")
+ ? new CacheControlHeaderValue () {
+ MaxAge = TimeSpan.FromDays (30) // Cache everything except json for 30 days
+ }
+ : new CacheControlHeaderValue () {
+ MaxAge = TimeSpan.FromMinutes (15) // Cache json for 15 minutes
+ };
+ }
+ });
- // Webpack middleware setup
- app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
- HotModuleReplacement = true
+ if (env.IsDevelopment ()) {
+ app.UseDeveloperExceptionPage ();
+ app.UseWebpackDevMiddleware (new WebpackDevMiddlewareOptions {
+ HotModuleReplacement = true,
+ HotModuleReplacementEndpoint = "/dist/"
+ });
+ app.UseSwagger ();
+ app.UseSwaggerUI (c => {
+ c.SwaggerEndpoint ("/swagger/v1/swagger.json", "My API V1");
});
- //Adding Seeder/Test Data
- AddTestData(app.ApplicationServices.GetService());
- }
- else
- {
- app.UseExceptionHandler("/Home/Error");
- }
-
- // CSRF / XSRF Token
- app.Use(async (context, next) =>
- {
- if (string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase))
- {
- var tokens = antiforgery.GetAndStoreTokens(context);
+ // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
- context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() {
- HttpOnly = false
+ app.MapWhen (x => !x.Request.Path.Value.StartsWith ("/swagger", StringComparison.OrdinalIgnoreCase), builder => {
+ builder.UseMvc (routes => {
+ routes.MapSpaFallbackRoute (
+ name: "spa-fallback",
+ defaults : new { controller = "Home", action = "Index" });
});
- }
- await next.Invoke();
- });
-
- app.UseStaticFiles();
-
- // ** MVC / WebAPI Routing & default SPA fallback Routing
- app.UseMvc(routes =>
- {
- routes.MapRoute(
- name: "default",
- template: "{controller=Home}/{action=Index}/{id?}");
+ });
+ } else {
+ app.UseMvc (routes => {
+ routes.MapRoute (
+ name: "default",
+ template: "{controller=Home}/{action=Index}/{id?}");
- routes.MapSpaFallbackRoute(
- name: "spa-fallback",
- defaults: new { controller = "Home", action = "Index" });
- });
- }
+ routes.MapRoute (
+ "Sitemap",
+ "sitemap.xml",
+ new { controller = "Home", action = "SitemapXml" });
- private static void AddTestData(SpaDbContext context)
- {
- Users testUser = new Users
- {
- Name = "Abrar Jahin"
- };
- context.User.Add(testUser);
+ routes.MapSpaFallbackRoute (
+ name: "spa-fallback",
+ defaults : new { controller = "Home", action = "Index" });
- context.SaveChanges();
+ });
+ app.UseExceptionHandler ("/Home/Error");
+ }
}
}
}
diff --git a/Views/Home/Index.cshtml b/Views/Home/Index.cshtml
index e2e92070..d8882a29 100644
--- a/Views/Home/Index.cshtml
+++ b/Views/Home/Index.cshtml
@@ -1,14 +1,13 @@
-@{
- ViewData["Title"] = ".NET Core + Angular + Universal !";
-}
+
+@Html.Raw(ViewData["SpaHtml"])
-
- ... Loading ...
-
-
+
+
-
@section scripts {
+
}
+
+@Html.Raw(ViewData["ServiceWorker"])
diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml
index bda4e592..e749bc3b 100644
--- a/Views/Shared/_Layout.cshtml
+++ b/Views/Shared/_Layout.cshtml
@@ -1,23 +1,26 @@
-
+
-
+
+
+
+ @ViewData["Title"]
+
+
+ @Html.Raw(ViewData["Meta"])
+ @Html.Raw(ViewData["Links"])
+
+
-
-
- @ViewData["Title"] - Angular Universal & ASP.NET Core Starter
-
+ @Html.Raw(ViewData["Styles"])
+
+
+
+ @RenderBody()
-
-
-
-
-
-
-
- @RenderBody()
-
- @RenderSection("scripts", required: false)
-
+
+ @Html.Raw(ViewData["TransferData"])
+ @Html.Raw(ViewData["Scripts"])
+ @RenderSection("scripts", required: false)
+
diff --git a/Views/_ViewImports.cshtml b/Views/_ViewImports.cshtml
index bae6ecef..1872fd6b 100644
--- a/Views/_ViewImports.cshtml
+++ b/Views/_ViewImports.cshtml
@@ -1,3 +1,3 @@
-@using Angular2Spa
+@using AspCoreServer
+@addTagHelper "*, Microsoft.AspNetCore.SpaServices"
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
-@addTagHelper "*, Microsoft.AspNetCore.SpaServices"
diff --git a/Views/_ViewStart.cshtml b/Views/_ViewStart.cshtml
index 7faa2282..820a2f6e 100644
--- a/Views/_ViewStart.cshtml
+++ b/Views/_ViewStart.cshtml
@@ -1,4 +1,3 @@
@{
Layout = "_Layout";
}
-
\ No newline at end of file
diff --git a/angular.json b/angular.json
new file mode 100644
index 00000000..b6c5377c
--- /dev/null
+++ b/angular.json
@@ -0,0 +1,107 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "AspnetCore-Angular-Universal": {
+ "root": "",
+ "sourceRoot": "ClientApp",
+ "projectType": "application",
+ "prefix": "app",
+ "schematics": {},
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:browser",
+ "options": {
+ "outputPath": "wwwroot/dist",
+ "main": "ClientApp/boot-browser.ts",
+ "polyfills": "ClientApp/polyfills/polyfills.ts",
+ "tsConfig": "ClientApp/tsconfig.app.json",
+ "assets": [],
+ "styles": [],
+ "scripts": []
+ },
+ "configurations": {
+ "production": {
+ "fileReplacements": [
+ {
+ "replace": "ClientApp/environments/environment.ts",
+ "with": "ClientApp/environments/environment.prod.ts"
+ }
+ ],
+ "optimization": true,
+ "outputHashing": "all",
+ "sourceMap": false,
+ "extractCss": true,
+ "namedChunks": false,
+ "aot": true,
+ "extractLicenses": true,
+ "vendorChunk": false,
+ "buildOptimizer": true
+ }
+ }
+ },
+ "serve": {
+ "builder": "@angular-devkit/build-angular:dev-server",
+ "options": {
+ "browserTarget": "AspnetCore-Angular-Universal:build"
+ },
+ "configurations": {
+ "production": {
+ "browserTarget": "AspnetCore-Angular-Universal:build:production"
+ }
+ }
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "AspnetCore-Angular-Universal:build"
+ }
+ },
+ "test": {
+ "builder": "@angular-devkit/build-angular:karma",
+ "options": {
+ "main": "ClientApp/test.ts",
+ "polyfills": "ClientApp/polyfills.ts",
+ "tsConfig": "ClientApp/tsconfig.spec.json",
+ "karmaConfig": "ClientApp/karma.conf.js",
+ "styles": ["ClientApp/styles.css"],
+ "scripts": [],
+ "assets": ["ClientApp/favicon.ico", "ClientApp/assets"]
+ }
+ },
+ "lint": {
+ "builder": "@angular-devkit/build-angular:tslint",
+ "options": {
+ "tsConfig": [
+ "ClientApp/tsconfig.app.json",
+ "ClientApp/tsconfig.spec.json"
+ ],
+ "exclude": ["**/node_modules/**"]
+ }
+ }
+ }
+ },
+ "AspnetCore-Angular-Universal-e2e": {
+ "root": "e2e/",
+ "projectType": "application",
+ "architect": {
+ "e2e": {
+ "builder": "@angular-devkit/build-angular:protractor",
+ "options": {
+ "protractorConfig": "e2e/protractor.conf.js",
+ "devServerTarget": "AspnetCore-Angular-Universal:serve"
+ }
+ },
+ "lint": {
+ "builder": "@angular-devkit/build-angular:tslint",
+ "options": {
+ "tsConfig": "e2e/tsconfig.e2e.json",
+ "exclude": ["**/node_modules/**"]
+ }
+ }
+ }
+ }
+ },
+ "defaultProject": "AspnetCore-Angular-Universal"
+}
diff --git a/app.json b/app.json
deleted file mode 100644
index 7a49d47d..00000000
--- a/app.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "name": "aspnetcore-angular2-universal",
- "description": "Deploy Angular 2+ Universal & ASP.NET Core SPA Advanced Starter on Heroku",
- "logo": "https://raw.githubusercontent.com/herokumx/herokumxnet/master/NETChatterGroup.png",
- "keywords": ["heroku", "asp.net-core", "angular2", "spa"],
- "env": {
- "NPM_CONFIG_PRODUCTION": {
- "description": "False as we need to install devDependencies on heroku instance (webpack, ...)",
- "value" : "true"
- }
- },
- "buildpacks": [
- {
- "url": "heroku/nodejs"
- },
- {
- "url": "https://github.com/ORuban/dotnet-core-buildpack.git"
- }
- ]
-}
diff --git a/appsettings.json b/appsettings.json
index 4cb2163b..723c096a 100644
--- a/appsettings.json
+++ b/appsettings.json
@@ -4,7 +4,7 @@
"LogLevel": {
"Default": "Debug",
"System": "Information",
- "Microsoft": "Information"
+ "Microsoft": "Information"
}
}
}
diff --git a/docker-compose.dcproj b/docker-compose.dcproj
new file mode 100644
index 00000000..920d4a2a
--- /dev/null
+++ b/docker-compose.dcproj
@@ -0,0 +1,18 @@
+
+
+
+ 2.1
+ Linux
+ 10b71bfc-c3ed-40b0-bb25-e38f04135e17
+ LaunchBrowser
+ {Scheme}://localhost:{ServicePort}
+ asp2017
+
+
+
+ docker-compose.yml
+
+
+
+
+
\ No newline at end of file
diff --git a/docker-compose.override.yml b/docker-compose.override.yml
new file mode 100644
index 00000000..dabcc4da
--- /dev/null
+++ b/docker-compose.override.yml
@@ -0,0 +1,9 @@
+version: '3.4'
+
+services:
+ asp2017:
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ #- ASPNETCORE_ENVIRONMENT=Production
+ ports:
+ - "80"
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..d7d80bba
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,8 @@
+version: '3.4'
+
+services:
+ asp2017:
+ image: ${DOCKER_REGISTRY}asp2017
+ build:
+ context: .
+ dockerfile: Dockerfile
diff --git a/docs/RESTAPI-ENTITY.MD b/docs/RESTAPI-ENTITY.MD
deleted file mode 100644
index 7a378a4b..00000000
--- a/docs/RESTAPI-ENTITY.MD
+++ /dev/null
@@ -1,43 +0,0 @@
-# WebAPI (REST API) - Entity Framework Core CRUD Examples:
-
-### Get all data
-
-
-
-
-
-If you need to use pagination, please crete your requesr URL like this-
-
-- Method - `GET`
-- URL-
-
- /api/user/all?currentPageNo=1&pageSize=5
-
-Default value (if u hit - `/api/user/all`)
-
- currentPageNo=1
- pageSize=20
-
-### Get a specific data
-
-
-
-
-
-### Insert data
-
-
-
-
-
-### Update data
-
-
-
-
-
-### Delete data
-
-
-
-
diff --git a/docs/all.jpeg b/docs/all.jpeg
deleted file mode 100644
index 12af165b..00000000
Binary files a/docs/all.jpeg and /dev/null differ
diff --git a/docs/angular2-seo.png b/docs/angular2-seo.png
new file mode 100644
index 00000000..96c9817a
Binary files /dev/null and b/docs/angular2-seo.png differ
diff --git a/docs/delete.jpeg b/docs/delete.jpeg
deleted file mode 100644
index ba1e8bfa..00000000
Binary files a/docs/delete.jpeg and /dev/null differ
diff --git a/docs/details.jpeg b/docs/details.jpeg
deleted file mode 100644
index ec188b91..00000000
Binary files a/docs/details.jpeg and /dev/null differ
diff --git a/docs/insert.jpeg b/docs/insert.jpeg
deleted file mode 100644
index 59b563c6..00000000
Binary files a/docs/insert.jpeg and /dev/null differ
diff --git a/docs/update.jpeg b/docs/update.jpeg
deleted file mode 100644
index 7ed97572..00000000
Binary files a/docs/update.jpeg and /dev/null differ
diff --git a/empty.js b/empty.js
deleted file mode 100644
index 30b0a568..00000000
--- a/empty.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Used in Prod & AoT builds to pass blank modules
-module.exports = {
- NgProbeToken: {},
- _createConditionalRootRenderer: function(rootRenderer, extraTokens, coreTokens) {
- return rootRenderer;
- },
- __platform_browser_private__: {}
-};
diff --git a/global.json b/global.json
deleted file mode 100644
index 7625a7b1..00000000
--- a/global.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "projects": [],
- "sdk": { "version": "1.0.0-preview2-1-003177" }
-}
diff --git a/helpers.js b/helpers.js
deleted file mode 100644
index 5dfc1a5f..00000000
--- a/helpers.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var path = require('path');
-var ROOT = path.resolve(__dirname);
-
-function hasProcessFlag(flag) {
- return process.argv.join('').indexOf(flag) > -1;
-}
-
-function root(args) {
- args = Array.prototype.slice.call(arguments, 0);
- return path.join.apply(path, [ROOT].concat(args));
-}
-
-
-exports.hasProcessFlag = hasProcessFlag;
-exports.root = root;
diff --git a/karma.conf.js b/karma.conf.js
deleted file mode 100644
index 67f8ba08..00000000
--- a/karma.conf.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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'],
- files: [
- './wwwroot/dist/vendor.js',
- './Client/karma-tests.ts'
- ],
- preprocessors: {
- './Client/karma-tests.ts': ['webpack']
- },
- reporters: ['progress'],
- port: 9876,
- colors: true,
- logLevel: config.LOG_INFO,
- autoWatch: true,
- browsers: ['Chrome'],
- mime: { 'application/javascript': ['ts','tsx'] },
- singleRun: false,
- webpack: require('./webpack.config.test.js'),
- webpackMiddleware: { stats: 'errors-only' }
- });
-};
diff --git a/package.json b/package.json
index 4557af8a..01bc52bc 100644
--- a/package.json
+++ b/package.json
@@ -1,147 +1,105 @@
{
- "name": "aspnetcore-angular2-universal-starter",
- "version": "1.0.0-rc.0",
+ "name": "angular7-aspnetcore-universal",
"author": {
- "name": "Mark Pieszak",
- "email": "mpieszak84@gmail.com",
- "url": "http://www.github.com/MarkPieszak"
+ "name": "Mark Pieszak | Trilon Consulting",
+ "email": "hello@trilon.io",
+ "url": "https://trilon.io/"
},
- "repository": {
- "type": "git",
- "url": "git+https://github.com/MarkPieszak/aspnetcore-angular2-universal.git"
- },
- "license": "MIT",
+ "version": "1.0.0-rc4",
"scripts": {
- "ci": "npm test && npm run e2e",
- "clean": "npm cache clean && npm run rimraf -- node_modules doc typings coverage wwwroot/dist",
- "clean:dist": "npm run rimraf -- wwwroot/dist",
- "docs": "npm run typedoc -- --options typedoc.json --exclude '**/*.spec.ts' ./Client/",
- "e2e": "npm run protractor",
- "e2e:live": "npm run e2e -- --elementExplorer",
- "lint": "npm run tslint \"Client/**/*.ts\"",
- "postinstall": "npm run webpack-vendor && npm run webpack",
- "posttest": "npm run coverage",
- "postversion": "git push && git push --tags",
- "pree2e": "npm run webdriver:update -- --standalone",
- "pretest": "npm run lint",
- "preversion": "npm test",
- "protractor": "protractor",
- "rimraf": "rimraf",
- "start:dashboard": "npm run webpack-dashboard-pre && npm run webpack-dashboard -- webpack-dev-server--inline --hot --progress --port 3001",
- "test": "karma start",
- "tslint": "tslint",
- "typedoc": "typedoc",
- "version": "npm run build",
- "webdriver:start": "npm run webdriver-manager start",
- "webdriver:update": "npm run webdriver-manager update",
- "webdriver-manager": "webdriver-manager",
- "webpack": "webpack --progress",
- "webpack-dashboard-pre": "npm run webpack -- --define dashboard",
- "webpack-dashboard": "webpack-dashboard",
- "webpack-vendor": "webpack --config webpack.config.vendor.js --progress"
+ "clean:install": "npm run clean && rimraf ./node_modules ./bin ./obj ./package-lock.json && dotnet restore Asp2017.csproj && npm i",
+ "lint": "tslint -p tsconfig.json",
+ "test": "npm run build:vendor && karma start ClientApp/test/karma.conf.js",
+ "test:watch": "npm run test -- --auto-watch --no-single-run",
+ "test:ci": "npm run test -- --browsers PhantomJS_custom",
+ "test:ci:watch": "npm run test:ci -- --auto-watch --no-single-run",
+ "test:coverage": "npm run test -- --coverage",
+ "build:dev": "npm run build:vendor && npm run build:webpack",
+ "build:webpack": "webpack --progress --color",
+ "build:prod": "npm run clean && npm run build:vendor -- --env.prod && npm run build:webpack -- --env.prod",
+ "build:p": "npm run build:webpack -- --env.prod",
+ "build:vendor": "webpack --config webpack.config.vendor.js --progress --color",
+ "clean": "rimraf wwwroot/dist clientapp/dist"
},
"dependencies": {
- "@angular/common": "2.4.7",
- "@angular/compiler": "2.4.7",
- "@angular/core": "2.4.7",
- "@angular/forms": "2.4.7",
- "@angular/http": "2.4.7",
- "@angular/platform-browser": "2.4.7",
- "@angular/platform-browser-dynamic": "2.4.7",
- "@angular/platform-server": "2.4.7",
- "@angular/router": "3.4.7",
- "@angularclass/hmr": "^1.2.2",
- "@ngrx/core": "1.2.0",
- "@ngrx/effects": "2.0.0",
- "@ngrx/store": "2.2.1",
- "@ngrx/store-devtools": "^3.2.3",
- "@types/chai": "^3.4.34",
- "@types/hammerjs": "2.0.34",
- "@types/jasmine": "2.5.40",
- "@types/jquery": "^2.0.39",
- "@types/node": "^6.0.46",
- "@types/protractor": "1.5.20",
- "@types/sinon": "1.16.31",
- "@types/source-map": "0.1.28",
- "@types/uglify-js": "2.6.28",
- "@types/webpack": "1.12.35",
- "angular2-template-loader": "0.6.0",
- "awesome-typescript-loader": "3.0.0-beta.13 || ^3.0.0",
- "angular2-platform-node": "~2.1.0-rc.1",
- "angular2-universal": "~2.1.0-rc.1",
- "angular2-universal-polyfills": "~2.1.0-rc.1",
- "aspnet-prerendering": "^2.0.0",
- "aspnet-webpack": "^1.0.27",
- "bootstrap": "^4.0.0-alpha.5",
- "core-js": "^2.4.1",
- "font-awesome": "^4.7.0",
- "immutable": "3.8.1",
- "css": "^2.2.1",
- "css-loader": "^0.25.0",
- "es6-promise": "3.2.1",
- "es6-promise-loader": "1.0.2",
- "expose-loader": "^0.7.1",
- "extract-text-webpack-plugin": "^2.0.0-rc",
- "file-loader": "^0.9.0",
- "html-loader": "^0.4.3",
+ "@angular/animations": "~7.2.0",
+ "@angular/common": "~7.2.0",
+ "@angular/compiler": "~7.2.0",
+ "@angular/core": "~7.2.0",
+ "@angular/forms": "~7.2.0",
+ "@angular/http": "~7.2.0",
+ "@angular/platform-browser": "~7.2.0",
+ "@angular/platform-browser-dynamic": "~7.2.0",
+ "@angular/platform-server": "~7.2.0",
+ "@angular/router": "~7.2.0",
+ "@nguniversal/aspnetcore-engine": "^7.1.0",
+ "@nguniversal/common": "^7.1.0",
+ "@ngx-translate/core": "^11.0.1",
+ "@ngx-translate/http-loader": "^4.0.0",
+ "@types/node": "^11.9.5",
+ "angular2-router-loader": "^0.3.5",
+ "angular2-template-loader": "^0.6.2",
+ "aspnet-prerendering": "^3.0.1",
+ "aspnet-webpack": "^3.0.0",
+ "awesome-typescript-loader": "^5.2.1",
+ "bootstrap": "^4.3.1",
+ "core-js": "^2.6.5",
+ "css": "^2.2.4",
+ "css-loader": "^2.1.0",
+ "event-source-polyfill": "^1.0.5",
+ "expose-loader": "^0.7.5",
+ "file-loader": "^3.0.1",
+ "html-loader": "^0.5.5",
"isomorphic-fetch": "^2.2.1",
- "js.clone": "0.0.3",
- "json-loader": "^0.5.4",
- "jquery": "^3.1.1",
- "ng2-bootstrap": "1.3.0",
- "preboot": "^4.5.2",
- "rxjs": "5.0.1",
- "signalr": "^2.2.1",
- "style-loader": "^0.13.1",
+ "jquery": "^3.3.1",
+ "json-loader": "^0.5.7",
+ "moment": "^2.24.0",
+ "ngx-bootstrap": "^3.2.0",
+ "node-sass": "^4.11.0",
+ "preboot": "^7.0.0",
+ "raw-loader": "^1.0.0",
+ "rimraf": "^2.6.3",
+ "rxjs": "6.2.2",
+ "sass-loader": "^7.1.0",
+ "style-loader": "^0.23.1",
"to-string-loader": "^1.1.5",
- "typed-immutable-record": "0.0.6",
- "zone.js": "0.7.2",
- "raw-loader": "^0.5.1",
- "reflect-metadata": "^0.1.8",
- "source-map-loader": "0.1.5",
- "ts-loader": "^1.1.0",
- "ts-node": "^1.7.0",
- "tslint": "3.15.1",
- "tslint-loader": "2.1.5",
- "typedoc": "0.5.1",
- "typescript": "2.0.10",
- "url-loader": "^0.5.7",
- "webpack": "^2.2.0",
- "webpack-dashboard": "0.3.0",
- "webpack-dev-middleware": "^1.9.0",
- "webpack-dev-server": "^2.2.0",
- "webpack-externals-plugin": "^1.0.0",
- "webpack-hot-middleware": "^2.15.0",
- "webpack-merge": "^0.15.0",
- "webpack-node-externals": "^1.5.4",
- "yargs": "^6.6.0"
+ "url-loader": "^1.1.2",
+ "webpack": "^4.29.5",
+ "webpack-hot-middleware": "^2.24.3",
+ "webpack-merge": "^4.2.1",
+ "zone.js": "^0.8.29"
},
"devDependencies": {
- "codelyzer": "1.0.0-beta.4",
- "karma": "1.3.0",
- "karma-chrome-launcher": "2.0.0",
- "karma-coverage": "1.1.1",
- "karma-jasmine": "1.0.2",
- "karma-mocha-reporter": "2.2.2",
- "karma-phantomjs-launcher": "1.0.2",
- "karma-remap-coverage": "0.1.2",
- "karma-source-map-support": "1.2.0",
- "karma-sourcemap-loader": "0.3.7",
- "karma-webpack": "1.8.0",
- "phantomjs-polyfill": "0.0.2",
- "phantomjs-prebuilt": "2.1.13",
- "protractor": "4.0.10"
+ "@angular-devkit/build-angular": "~0.13.3",
+ "@angular/cli": "~7.3.3",
+ "@angular/compiler-cli": "~7.2.0",
+ "@ngtools/webpack": "~7.3.3",
+ "@types/jasmine": "~2.8.8",
+ "codelyzer": "~4.5.0",
+ "istanbul-instrumenter-loader": "^3.0.1",
+ "jasmine-core": "^3.3.0",
+ "jasmine-spec-reporter": "^4.2.1",
+ "karma": "~4.0.0",
+ "karma-chrome-launcher": "~2.2.0",
+ "karma-coverage": "~1.1.2",
+ "karma-jasmine": "~2.0.1",
+ "karma-mocha-reporter": "^2.2.5",
+ "karma-phantomjs-launcher": "^1.0.4",
+ "karma-remap-coverage": "^0.1.5",
+ "karma-sourcemap-loader": "^0.3.7",
+ "karma-webpack": "^3.0.5",
+ "mini-css-extract-plugin": "^0.5.0",
+ "terser-webpack-plugin": "^1.2.3",
+ "tslint": "~5.11.0",
+ "typescript": "~3.2.2",
+ "uglifyjs-webpack-plugin": "^2.1.2",
+ "webpack-bundle-analyzer": "^3.0.4",
+ "webpack-cli": "^3.2.3"
+ },
+ "license": "MIT",
+ "repository": {
+ "type": "github",
+ "url": "https://github.com/MarkPieszak/aspnetcore-angular2-universal"
},
- "keywords": [
- "aspnetcore",
- "angular2",
- "angular2-universal",
- "angular2universal",
- "universal",
- "typescript",
- "typescript2",
- "ts2",
- "webpack",
- "webpack2"
- ]
-}
\ No newline at end of file
+ "readme": "https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/master/README.md"
+}
diff --git a/project.json b/project.json
deleted file mode 100644
index acb89575..00000000
--- a/project.json
+++ /dev/null
@@ -1,89 +0,0 @@
-{
- "buildOptions": {
- "compile": {
- "exclude": [
- "node_modules"
- ]
- },
- "emitEntryPoint": true,
- "preserveCompilationContext": true
- },
- "dependencies": {
- "Gray.Microsoft.AspNetCore.SignalR.Server": "0.2.0-alpha1",
- "Microsoft.ApplicationInsights.AspNetCore": "1.0.0",
- "Microsoft.AspNetCore.Diagnostics": "1.1.0",
- "Microsoft.AspNetCore.Mvc": "1.1.1",
- "Microsoft.AspNetCore.Razor.Tools": {
- "type": "build",
- "version": "1.0.0-preview2-final"
- },
- "Microsoft.AspNetCore.Cors": "1.1.0",
- "Microsoft.AspNetCore.Antiforgery": "1.1.0",
- "Microsoft.AspNetCore.Authentication.Cookies": "1.1.0",
- "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
- "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
- "Microsoft.AspNetCore.SpaServices": "1.1.0-*",
- "Microsoft.AspNetCore.StaticFiles": "1.1.0",
- "Microsoft.EntityFrameworkCore.Relational": "1.1.0",
- "Microsoft.EntityFrameworkCore.Sqlite": "1.1.0",
- "Microsoft.Extensions.Caching.Memory": "1.1.0",
- "Microsoft.Extensions.Configuration.CommandLine": "1.1.0",
- "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
- "Microsoft.Extensions.Configuration.Json": "1.1.0",
- "Microsoft.Extensions.Logging": "1.1.0",
- "Microsoft.Extensions.Logging.Console": "1.1.0",
- "Microsoft.Extensions.Logging.Debug": "1.1.0",
- "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
- "Microsoft.NETCore.App": {
- "type": "platform",
- "version": "1.1.0"
- },
- "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.1.0",
- "Newtonsoft.Json": "9.0.1"
- },
- "frameworks": {
- "netcoreapp1.1": {
- "imports": [
- "dotnet5.6",
- "portable-net45+win8"
- ]
- }
- },
- "publishOptions": {
- "include": [
- "appsettings.json",
- "Client",
- "Views",
- "web.config",
- "webpack.*.js",
- "wwwroot"
- ],
- "exclude": [
- "wwwroot/dist/*.map"
- ]
- },
- "runtimeOptions": {
- "configProperties": {
- "System.GC.Server": true
- }
- },
- "scripts": {
- "prepublish": [
- "npm install",
- "node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod",
- "node node_modules/webpack/bin/webpack.js --env.prod"
- ],
- "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
- },
- "tooling": {
- "defaultNamespace": "Angular2Spa"
- },
- "tools": {
- "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
- "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final",
- "Microsoft.DotNet.Watcher.Tools": "1.0.0-preview2-final",
- "Microsoft.EntityFrameworkCore.Relational.Design": "1.1.0",
- "Microsoft.EntityFrameworkCore.Relational.Design.Specification.Tests": "1.1.0",
- "Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final"
- }
-}
diff --git a/protractor.conf.js b/protractor.conf.js
deleted file mode 100644
index 07d51b9e..00000000
--- a/protractor.conf.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * @author: @AngularClass
- */
-
-require('ts-node/register');
-var helpers = require('./helpers');
-
-exports.config = {
- baseUrl: 'http://localhost:5000/',
-
- // use `npm run e2e`
- specs: [
- helpers.root('Client/**/**.e2e.ts'),
- helpers.root('Client/**/*.e2e.ts')
- ],
- exclude: [],
-
- framework: 'jasmine2',
-
- allScriptsTimeout: 110000,
-
- jasmineNodeOpts: {
- showTiming: true,
- showColors: true,
- isVerbose: false,
- includeStackTrace: false,
- defaultTimeoutInterval: 400000
- },
- directConnect: true,
-
- capabilities: {
- 'browserName': 'chrome',
- 'chromeOptions': {
- 'args': ['show-fps-counter=true']
- }
- },
-
- onPrepare: function () {
- browser.ignoreSynchronization = true;
- },
-
- /**
- * Angular 2 configuration
- *
- * useAllAngular2AppRoots: tells Protractor to wait for any angular2 apps on the page instead of just the one matching
- * `rootEl`
- */
- useAllAngular2AppRoots: true
-};
\ No newline at end of file
diff --git a/spec-bundle.js b/spec-bundle.js
deleted file mode 100644
index 9768ebfe..00000000
--- a/spec-bundle.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * When testing with webpack and ES6, we have to do some extra
- * things to get testing to work right. Because we are gonna write tests
- * in ES6 too, we have to compile those as well. That's handled in
- * karma.conf.js with the karma-webpack plugin. This is the entry
- * file for webpack test. Just like webpack will create a bundle.js
- * file for our client, when we run test, it will compile and bundle them
- * all here! Crazy huh. So we need to do some setup
- */
-Error.stackTraceLimit = Infinity;
-
-require('core-js/es6');
-require('core-js/es7/reflect');
-
-// Typescript emit helpers polyfill
-require('ts-helpers');
-
-require('zone.js/dist/zone');
-require('zone.js/dist/long-stack-trace-zone');
-require('zone.js/dist/proxy'); // since zone.js 0.6.15
-require('zone.js/dist/sync-test');
-require('zone.js/dist/jasmine-patch'); // put here since zone.js 0.6.14
-require('zone.js/dist/async-test');
-require('zone.js/dist/fake-async-test');
-
-// RxJS
-require('rxjs/Rx');
-
-var testing = require('@angular/core/testing');
-var browser = require('@angular/platform-browser-dynamic/testing');
-
-testing.TestBed.initTestEnvironment(
- browser.BrowserDynamicTestingModule,
- browser.platformBrowserDynamicTesting()
-);
-
-/*
- * Ok, this is kinda crazy. We can use the context method on
- * require that webpack created in order to tell webpack
- * what files we actually want to require or import.
- * Below, context will be a function/object with file names as keys.
- * Using that regex we are saying look in ../src then find
- * any file that ends with spec.ts and get its path. By passing in true
- * we say do this recursively
- */
-var testContext = require.context('../Client', true, /\.spec\.ts/);
-
-/*
- * get all the files, for each file, call the context function
- * that will require the file and load it up here. Context will
- * loop and require those spec files here
- */
-function requireAll(requireContext) {
- return requireContext.keys().map(requireContext);
-}
-
-// requires and returns all modules that match
-var modules = requireAll(testContext);
diff --git a/tsconfig.json b/tsconfig.json
index 8942fb0d..23317dc5 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,38 +1,23 @@
-{
+{
"compilerOptions": {
"moduleResolution": "node",
+ "module": "es2015",
"target": "es5",
+ "alwaysStrict": true,
+ "noImplicitAny": false,
"sourceMap": true,
- "rootDir": ".",
- "baseUrl": ".",
- "outDir": "wwwroot/dist",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipDefaultLibCheck": true,
- "lib": [ "es6", "dom" ],
- "types": [
- "hammerjs",
- "jasmine",
- "node",
- "source-map",
- "selenium-webdriver",
- "uglify-js",
- "webpack"
+ "skipLibCheck": true,
+ "allowUnreachableCode": false,
+ "lib": [
+ "es2016",
+ "dom"
],
- "paths": {
- "app" : ["./Client/app" ],
- "app-shared": [ "./Client/shared" ],
- "app-containers": [ "./Client/containers" ],
- "app-components": [ "./Client/components" ]
- }
+ "types": [ "node" ]
},
- "exclude": [ "bin", "wwwroot", "node_modules" ],
- "compileOnSave": false,
- "awesomeTypescriptLoaderOptions": {
- "forkChecker": true,
- "useWebpackText": true,
- "useCache": true
- },
- "buildOnSave": false,
- "atom": { "rewriteTsconfig": false }
+ "include": [
+ "ClientApp"
+ ]
}
diff --git a/tslint.json b/tslint.json
index dbbdcd6d..f318a0b8 100644
--- a/tslint.json
+++ b/tslint.json
@@ -1,7 +1,5 @@
-{
- "rulesDirectory": [
- "node_modules/codelyzer"
- ],
+{
+ "defaultSeverity": "warn",
"rules": {
"align": false,
"ban": false,
@@ -11,32 +9,20 @@
"check-space"
],
"component-class-suffix": true,
- "component-selector-name": [
+ "component-selector": [
true,
+ "element",
+ "app",
"kebab-case"
],
- "component-selector-prefix": [
- true,
- "app"
- ],
- "component-selector-type": [
- true,
- "element"
- ],
"curly": false,
"directive-class-suffix": true,
- "directive-selector-name": [
+ "directive-selector": [
true,
+ "attribute",
+ "app",
"camelCase"
],
- "directive-selector-prefix": [
- true,
- "app"
- ],
- "directive-selector-type": [
- true,
- "attribute"
- ],
"eofline": true,
"forin": true,
"import-destructuring-spacing": true,
@@ -47,7 +33,6 @@
"interface-name": false,
"jsdoc-format": true,
"label-position": true,
- "label-undefined": true,
"max-line-length": [
true,
200
@@ -76,7 +61,6 @@
"no-construct": true,
"no-constructor-vars": false,
"no-debugger": true,
- "no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-eval": true,
@@ -91,9 +75,8 @@
"no-string-literal": false,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": false,
- "no-unreachable": true,
"no-unused-expression": true,
- "no-unused-variable": false,
+ "no-unused-variable": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"no-var-requires": false,
@@ -156,10 +139,6 @@
"use-life-cycle-interface": true,
"use-output-property-decorator": true,
"use-pipe-transform-interface": true,
- "use-strict": [
- true,
- "check-module"
- ],
"variable-name": [
true,
"check-format",
@@ -174,5 +153,8 @@
"check-separator",
"check-type"
]
- }
-}
\ No newline at end of file
+ },
+ "rulesDirectory": [
+ "node_modules/codelyzer"
+ ]
+}
diff --git a/typedoc.json b/typedoc.json
deleted file mode 100644
index f5a61c45..00000000
--- a/typedoc.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "mode": "modules",
- "out": "doc",
- "theme": "default",
- "ignoreCompilerErrors": "true",
- "experimentalDecorators": "true",
- "emitDecoratorMetadata": "true",
- "target": "ES5",
- "moduleResolution": "node",
- "preserveConstEnums": "true",
- "stripInternal": "true",
- "suppressExcessPropertyErrors": "true",
- "suppressImplicitAnyIndexErrors": "true",
- "module": "commonjs"
-}
diff --git a/webpack.additions.js b/webpack.additions.js
new file mode 100644
index 00000000..9c63a2f8
--- /dev/null
+++ b/webpack.additions.js
@@ -0,0 +1,20 @@
+/* [ Webpack Additions ]
+ *
+ * This file contains ADD-ONS we are adding on-top of the traditional JavaScriptServices repo
+ * We do this so that those already using JavaScriptServices can easily figure out how to combine this repo into it.
+ */
+
+// Shared rules[] we need to add
+const sharedModuleRules = [
+ // sass
+ {
+ test: /\.scss$/,
+ loaders: ['to-string-loader', 'css-loader', 'sass-loader']
+ },
+ // font-awesome
+ { test: /\.(woff2?|ttf|eot|svg)$/, loader: 'url-loader?limit=10000' }
+];
+
+module.exports = {
+ sharedModuleRules
+};
diff --git a/webpack.config.js b/webpack.config.js
index 801c426f..249c4f59 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,267 +1,216 @@
-// Webpack file
-// Author : Mark Pieszak
+/*
+ * Webpack (JavaScriptServices) with a few changes & updates
+ * - This is to keep us inline with JSServices, and help those using that template to add things from this one
+ *
+ * Things updated or changed:
+ * module -> rules []
+ * .ts$ test : Added 'angular2-router-loader' for lazy-loading in development
+ * added ...sharedModuleRules (for scss & font-awesome loaders)
+ */
-const webpack = require('webpack');
const path = require('path');
-const clone = require('js.clone');
+const webpack = require('webpack');
const merge = require('webpack-merge');
-const argv = require('yargs').argv;
-
-// TS/Webpack things to enable paths:[] from tsconfig
-// This helps Webpack understand what they are, and where they are coming from so it can "map" them
-// & create the modules correctly
-const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin');
-const TsConfigPathsPlugin = require('awesome-typescript-loader').TsConfigPathsPlugin;
+const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin;
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
-const DefinePlugin = require('webpack/lib/DefinePlugin');
-const DashboardPlugin = require('webpack-dashboard/plugin');
-
-// Test if Development build from ASPNETCore environment
-var isDevBuild = process.env.ASPNETCORE_ENVIRONMENT === 'Production' ? false : true;
-
-// Sourcemaps (for Development only)
-var devTool = isDevBuild ? 'source-map' : '';
-
-// Webpack configuration Export
-// Here we wrap the configuration around setTypeScriptAlias so that it can properly map our "paths: []" from tsconfig
-// to the correct corresponding module resource locations
-module.exports = (env) => {
-
- const isDashboardMode = argv.define === 'dashboard';
-
- console.log('isDashboardMode? ' + isDashboardMode);
-
- // production is passed with --env.prod
- const isDevBuild = !(env && env.prod);
-
- console.log('isDevBuild? ' + isDevBuild);
-
- // Where our "Dist" (distribution) directory is
- const distDirectory = './wwwroot/dist';
-
- /*
- * - Shared webpack configuration -
- * common things between Client & Server bundles
- */
- const sharedConfig = {
- // What types of files should webpack handle
- resolve: {
- extensions: ['.js', '.ts']
+const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
+ .BundleAnalyzerPlugin;
+const TerserPlugin = require('terser-webpack-plugin');
+
+const { sharedModuleRules } = require('./webpack.additions');
+
+module.exports = env => {
+ // Configuration in common to both client-side and server-side bundles
+ const isDevBuild = !(env && env.prod);
+ const sharedConfig = {
+ mode: isDevBuild ? 'development' : 'production',
+ stats: {
+ modules: false
+ },
+ context: __dirname,
+ resolve: {
+ extensions: ['.js', '.ts']
+ },
+ output: {
+ filename: '[name].js',
+ publicPath: 'dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
+ },
+ module: {
+ rules: [
+ {
+ test: /^(?!.*\.spec\.ts$).*\.ts$/,
+ use: isDevBuild
+ ? [
+ 'awesome-typescript-loader?silent=true',
+ 'angular2-template-loader',
+ 'angular2-router-loader'
+ ]
+ : '@ngtools/webpack'
},
-
- stats: { modules: false },
- context: __dirname,
-
- output: {
- filename: '[name].js',
- // Webpack dev middleware, if enabled, handles requests for this URL prefix
- publicPath: '/dist/'
- },
-
- // Here we tell Webpack how to handle Modules
- module: {
-
- // I wish this was just called "Processors" instead of Loaders
- // Think of this as, test the regex against each file webpack runs into, if it matches that type,
- // Use the following "Loaders" or processors, to -handle- that file
- // (whether it be to compile it, parse it, or run some other magic on it)
- loaders: [
-
- { // TypeScript files
- test: /\.ts$/,
- exclude: [/\.(spec|e2e)\.ts$/], // Exclude test files | end2end test spec files etc
- loaders: [
- 'awesome-typescript-loader?silent=true', // Amazing TS loader
- 'angular2-template-loader' // Handles templateUrl stylesUrl and automatically just inserts them into the template|styles
- // instead of having the module resource loader handle it
- ]
- },
-
- // Html files
- { test: /\.html$/, loader: 'raw-loader' },
-
- // CSS files
- { test: /\.css/, loader: 'raw-loader' },
-
- // SASS files
- //{ test: /\.scss$/, loaders: ['raw-loader', 'sass-loader?sourceMap'] },
-
- // JSON files
- { test: /\.json$/, loader: 'json-loader' },
-
- // Image files
- { test: /\.(png|jpg|jpeg|gif)$/, use: 'url-loader?limit=25000' },
-
- // Font files of all types
- {
- test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=10000&mimetype=application/font-woff"
- }, {
- test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=10000&mimetype=application/font-woff"
- }, {
- test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=10000&mimetype=application/octet-stream"
- }, {
- test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
- loader: "file"
- },
- // SVG files
- {
- test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=25000&mimetype=image/svg+xml"
- }]
+ {
+ test: /\.html$/,
+ use: 'html-loader?minimize=false'
},
-
- // Plugins are middleware we can use during webpacks processing cycles to handle other things we want to do
- plugins: [
- new CheckerPlugin()
-
- ].concat(isDashboardMode ? [ new DashboardPlugin({ port: 3001 }) ] : [])
-
- };
-
- const clientConfig = setTypeScriptAlias(require('./tsconfig.json'), {
-
- // What are our "entry" points for webpack to create "Chunks"
- entry: {
- 'main-client' : './Client/bootstrap-client.ts'
+ {
+ test: /\.css$/,
+ use: [
+ 'to-string-loader',
+ isDevBuild ? 'css-loader' : 'css-loader?minimize'
+ ]
},
-
- // Where should webpack put files after they're processed
- output: {
- // Path location
- path: path.join(__dirname, distDirectory),
- // This is a dynamic way to handle multiple entry[] files [name] in our case would be the Key for each one
- // ex: main.js | vendor.js | etc
- filename: '[name].js',
- // public location, (you could have them pointing to external CDNs here for example)
- publicPath: '/dist/'
+ {
+ test: /\.(png|jpg|jpeg|gif|svg)$/,
+ use: 'url-loader?limit=25000'
},
-
- // Plugins are middleware we can use during webpacks processing cycles to handle other things we want to do
- // Here we need to fix some Angular2 issues
- // Handle TsConfig paths:[] so webpack is aware of them and it's actually able to process them correctly
- // Also we need to let our development webpack file be aware of our separate VENDOR webpack file
- // (the vendor file has all the 3rd party things we need to get our project going)
- plugins: [
-
- // Define a variable that can be used in the Code
- // process.env.development === true
- new DefinePlugin({
- 'process.env': {
- 'development': true
- }
- }),
-
- new ContextReplacementPlugin(
- // The (\\|\/) piece accounts for path separators in *nix and Windows
- /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
- root('./Client')
- ),
-
- new TsConfigPathsPlugin({
- tsconfig: 'tsconfig.json'
- }),
-
- new webpack.DllReferencePlugin({
- context: __dirname,
- manifest: require('./wwwroot/dist/vendor-manifest.json')
- })
-
- ].concat(isDevBuild ? [
- // Development ONLY plugins (for the Client bundle)
-
+ ...sharedModuleRules
+ ]
+ },
+ plugins: [new CheckerPlugin()]
+ };
+
+ // Configuration for client-side bundle suitable for running in browsers
+ const clientBundleOutputDir = './wwwroot/dist';
+ const clientBundleConfig = merge(sharedConfig, {
+ entry: {
+ 'main-client': './ClientApp/boot.browser.ts'
+ },
+ output: {
+ path: path.join(__dirname, clientBundleOutputDir)
+ },
+ plugins: [
+ new webpack.DllReferencePlugin({
+ context: __dirname,
+ manifest: require('./wwwroot/dist/vendor-manifest.json')
+ })
+ ].concat(
+ isDevBuild
+ ? [
+ // Plugins that apply in development builds only
new webpack.SourceMapDevToolPlugin({
- // Remove this line if you prefer inline source maps
- filename: '[file].map',
- moduleFilenameTemplate: path.relative(distDirectory, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
+ filename: '[file].map', // Remove this line if you prefer inline source maps
+ moduleFilenameTemplate: path.relative(
+ clientBundleOutputDir,
+ '[resourcePath]'
+ ) // Point sourcemap entries to the original file locations on disk
})
-
- ] : [
- // Production ONLY plugins (for the CLIENT bundle)
- new DefinePlugin({
- 'process.env': {
- 'production': true
- }
- }),
-
- // problem with platformUniversalDynamic on the server/client
- new webpack.NormalModuleReplacementPlugin(
- /@angular(\\|\/)compiler/,
- root('empty.js')
+ ]
+ : [
+ // new BundleAnalyzerPlugin(),
+ // Plugins that apply in production builds only
+ new AngularCompilerPlugin({
+ mainPath: path.join(__dirname, 'ClientApp/boot.browser.ts'),
+ tsConfigPath: './ClientApp/tsconfig.app.json',
+ entryModule: path.join(
+ __dirname,
+ 'ClientApp/app/app.module.browser#AppModule'
+ ),
+ exclude: ['./**/*.server.ts'],
+ sourceMap: isDevBuild
+ })
+ ]
+ ),
+ devtool: isDevBuild ? 'cheap-eval-source-map' : false,
+ node: {
+ fs: 'empty'
+ },
+ optimization: {
+ minimizer: [].concat(
+ isDevBuild
+ ? []
+ : [
+ // we specify a custom TerserPlugin here to get source maps in production
+ new TerserPlugin({
+ sourceMap: true,
+ terserOptions: {
+ compress: true,
+ ecma: 6,
+ mangle: true,
+ keep_classnames: true,
+ keep_fnames: true,
+ },
+ }),
+ ]
+ )
+ }
+ });
+
+ // Configuration for server-side (prerendering) bundle suitable for running in Node
+ const serverBundleConfig = merge(sharedConfig, {
+ // resolve: { mainFields: ['main'] },
+ entry: {
+ 'main-server': isDevBuild
+ ? './ClientApp/boot.server.ts'
+ : './ClientApp/boot.server.PRODUCTION.ts'
+ },
+ plugins: [
+ new webpack.DllReferencePlugin({
+ context: __dirname,
+ manifest: require('./ClientApp/dist/vendor-manifest.json'),
+ sourceType: 'commonjs2',
+ name: './vendor'
+ })
+ ].concat(
+ isDevBuild
+ ? [
+ new webpack.ContextReplacementPlugin(
+ // fixes WARNING Critical dependency: the request of a dependency is an expression
+ /(.+)?angular(\\|\/)core(.+)?/,
+ path.join(__dirname, 'src'), // location of your src
+ {} // a map of your routes
),
-
- // Uglify (minify/etc)
- new webpack.optimize.UglifyJsPlugin()
- ])
- });
-
- // Configuration for server-side (prerendering) bundle suitable for running in Node
- const serverConfig = setTypeScriptAlias(require('./tsconfig.json'), {
- resolve: { mainFields: ['main'] },
- entry: { 'main-server': './Client/bootstrap-server.ts' },
- plugins: [
- new webpack.DllReferencePlugin({
- context: __dirname,
- manifest: require('./Client/dist/vendor-manifest.json'),
- sourceType: 'commonjs2',
- name: './vendor'
+ new webpack.ContextReplacementPlugin(
+ // fixes WARNING Critical dependency: the request of a dependency is an expression
+ /(.+)?express(\\|\/)(.+)?/,
+ path.join(__dirname, 'src'),
+ {}
+ )
+ ]
+ : [
+ // Plugins that apply in production builds only
+ new AngularCompilerPlugin({
+ mainPath: path.join(
+ __dirname,
+ 'ClientApp/boot.server.PRODUCTION.ts'
+ ),
+ tsConfigPath: './ClientApp/tsconfig.app.json',
+ entryModule: path.join(
+ __dirname,
+ 'ClientApp/app/app.module.server#AppModule'
+ ),
+ exclude: ['./**/*.browser.ts'],
+ sourceMap: isDevBuild
})
- ],
- output: {
- libraryTarget: 'commonjs',
- path: path.join(__dirname, './Client/dist')
- },
- target: 'node',
- devtool: 'inline-source-map',
-
- node: {
- global: true,
- crypto: 'empty',
- process: true,
- module: false,
- clearImmediate: false,
- setImmediate: false
- }
- });
-
-
- // Merge with Shared
- const mergedClientConfig = merge(sharedConfig, clientConfig);
- const mergedServerConfig = merge(sharedConfig, serverConfig);
-
- // Expose both Webpack bundle configurations
- return [
- mergedClientConfig,
- mergedServerConfig
- ];
-
-};
-
-
-////////////////////////////////////////////////
-// Internal methods
-//////////////////
-
-function setTypeScriptAlias(tsConfig, config) {
- var newConfig = clone(config);
- newConfig = newConfig || {};
- newConfig.resolve = newConfig.resolve || {};
- newConfig.resolve.alias = newConfig.resolve.alias || {};
- var tsPaths = tsConfig.compilerOptions.paths;
- for (var prop in tsPaths) {
- newConfig.resolve.alias[prop] = root(tsPaths[prop][0]);
+ ]
+ ),
+ output: {
+ libraryTarget: 'commonjs',
+ path: path.join(__dirname, './ClientApp/dist')
+ },
+ target: 'node',
+ // switch to "inline-source-map" if you want to debug the TS during SSR
+ devtool: isDevBuild ? 'cheap-eval-source-map' : false,
+ optimization: {
+ minimizer: [].concat(
+ isDevBuild
+ ? []
+ : [
+ // we specify a custom TerserPlugin here to get source maps in production
+ new TerserPlugin({
+ cache: true,
+ parallel: true,
+ sourceMap: true,
+ terserOptions: {
+ compress: false,
+ ecma: 6,
+ mangle: true,
+ keep_classnames: true,
+ keep_fnames: true,
+ },
+ })
+ ]
+ )
}
- return newConfig;
-}
-
-function plugins(plugins, config) {
- config.plugins = config.plugins.concat(plugins);
- return config
-}
+ });
-function root(args) {
- args = Array.prototype.slice.call(arguments, 0);
- return path.join.apply(path, [__dirname].concat(args));
-}
+ return [clientBundleConfig, serverBundleConfig];
+};
diff --git a/webpack.config.test.js b/webpack.config.test.js
deleted file mode 100644
index 44455a05..00000000
--- a/webpack.config.test.js
+++ /dev/null
@@ -1,437 +0,0 @@
-
-// Webpack file
-// Author : Mark Pieszak
-
-var webpack = require('webpack');
-var path = require('path');
-var clone = require('js.clone');
-
-// TS/Webpack things to enable paths:[] from tsconfig
-// This helps Webpack understand what they are, and where they are coming from so it can "map" them
-// & create the modules correctly
-var ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin');
-var TsConfigPathsPlugin = require('awesome-typescript-loader').TsConfigPathsPlugin;
-var DefinePlugin = require('webpack/lib/DefinePlugin');
-
-// Test if Development build from ASPNETCore environment
-var isDevBuild = process.env.ASPNETCORE_ENVIRONMENT === 'Production' ? false : true;
-
-// Sourcemaps (for Development only)
-var devTool = isDevBuild ? 'source-map' : '';
-
-// Webpack configuration Export
-// Here we wrap the configuration around setTypeScriptAlias so that it can properly map our "paths: []" from tsconfig
-// to the correct corresponding module resource locations
-module.exports = setTypeScriptAlias(require('./tsconfig.json'), {
-
- devtool: 'inline-source-map',
-
- // What types of files should webpack handle
- resolve: {
- extensions: ['.js', '.ts']
- },
-
- // What are our "entry" points for webpack to create "Chunks"
- entry: {
- main: ['./Client/bootstrap-client.ts']
- },
-
- // Where should webpack put files after they're processed
- output: {
- // Path location
- path: path.join(__dirname, 'wwwroot', 'dist'),
- // This is a dynamic way to handle multiple entry[] files [name] in our case would be the Key for each one
- // ex: main.js | vendor.js | etc
- filename: '[name].js',
- // public location, (you could have them pointing to external CDNs here for example)
- publicPath: '/dist/'
- },
-
- // Here we tell Webpack how to handle Modules
- module: {
-
- // I wish this was just called "Processors" instead of Loaders
- // Think of this as, test the regex against each file webpack runs into, if it matches that type,
- // Use the following "Loaders" or processors, to -handle- that file
- // (whether it be to compile it, parse it, or run some other magic on it)
- loaders: [
-
- { // TypeScript files
- test: /\.ts$/,
- exclude: [/\.e2e\.ts$/], // Exclude test files | end2end test spec files etc
- loaders: [
- 'awesome-typescript-loader', // Amazing TS loader
- 'angular2-template-loader' // Handles templateUrl stylesUrl and automatically just inserts them into the template|styles
- // instead of having the module resource loader handle it
- ]
- },
-
- // Html files
- { test: /\.html$/, loader: 'raw-loader' },
-
- // CSS files
- { test: /\.css/, loader: 'raw-loader' },
-
- // SASS files
- //{ test: /\.scss$/, loaders: ['raw-loader', 'sass-loader?sourceMap'] },
-
- // JSON files
- { test: /\.json$/, loader: 'json-loader' },
-
- // Font files of all types
- {
- test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=10000&mimetype=application/font-woff"
- }, {
- test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=10000&mimetype=application/font-woff"
- }, {
- test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=10000&mimetype=application/octet-stream"
- }, {
- test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
- loader: "file"
- }, {
- test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
- loader: "url?limit=10000&mimetype=image/svg+xml"
- }]
- },
-
- // Plugins are middleware we can use during webpacks processing cycles to handle other things we want to do
- // Here we need to fix some Angular2 issues
- // Handle TsConfig paths:[] so webpack is aware of them and it's actually able to process them correctly
- // Also we need to let our development webpack file be aware of our separate VENDOR webpack file
- // (the vendor file has all the 3rd party things we need to get our project going)
- plugins: [
-
- new DefinePlugin({
- 'process.env': {
- 'development': true
- }
- }),
-
- new ContextReplacementPlugin(
- // The (\\|\/) piece accounts for path separators in *nix and Windows
- /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
- root('./Client')
- ),
-
- new TsConfigPathsPlugin({
- tsconfig: 'tsconfig.json'
- }),
-
- new webpack.DllReferencePlugin({
- context: '.',
- manifest: require('./wwwroot/dist/vendor-manifest.json')
- })
-
- ].concat(isDevBuild ? [] : [
- // Plugins that apply in production builds only
- new DefinePlugin({
- 'process.env': {
- 'production': true
- }
- }),
-
- // problem with platformUniversalDynamic on the server/client
- new webpack.NormalModuleReplacementPlugin(
- /@angular(\\|\/)compiler/,
- root('empty.js')
- ),
-
- //new webpack.optimize.OccurrenceOrderPlugin(), <-- not needed in webpack2 anymore
- new webpack.optimize.UglifyJsPlugin()
- ]),
-
- node: {
- global: true,
- crypto: 'empty',
- process: true,
- module: false,
- clearImmediate: false,
- setImmediate: false
- }
-});
-
-
-////////////////////////////////////////////////
-// Internal methods
-//////////////////
-
-function setTypeScriptAlias(tsConfig, config) {
- var newConfig = clone(config);
- newConfig = newConfig || {};
- newConfig.resolve = newConfig.resolve || {};
- newConfig.resolve.alias = newConfig.resolve.alias || {};
- var tsPaths = tsConfig.compilerOptions.paths;
- for (var prop in tsPaths) {
- newConfig.resolve.alias[prop] = root(tsPaths[prop][0]);
- }
- return newConfig;
-}
-
-function plugins(plugins, config) {
- config.plugins = config.plugins.concat(plugins);
- return config
-}
-
-function root(args) {
- args = Array.prototype.slice.call(arguments, 0);
- return path.join.apply(path, [__dirname].concat(args));
-}
-
-
-
-
-// const helpers = require('./helpers');
-// const path = require('path');
-
-// /**
-// * Webpack Plugins
-// */
-// const ProvidePlugin = require('webpack/lib/ProvidePlugin');
-// const DefinePlugin = require('webpack/lib/DefinePlugin');
-// const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin');
-// const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin');
-
-// /**
-// * Webpack Constants
-// */
-// const ENV = process.env.ENV = process.env.NODE_ENV = 'test';
-
-// /**
-// * Webpack configuration
-// *
-// * See: http://webpack.github.io/docs/configuration.html#cli
-// */
-// module.exports = function (options) {
-// return {
-
-// /**
-// * Source map for Karma from the help of karma-sourcemap-loader & karma-webpack
-// *
-// * Do not change, leave as is or it wont work.
-// * See: https://github.com/webpack/karma-webpack#source-maps
-// */
-// devtool: 'inline-source-map',
-
-// /**
-// * Options affecting the resolving of modules.
-// *
-// * See: http://webpack.github.io/docs/configuration.html#resolve
-// */
-// resolve: {
-
-// /**
-// * An array of extensions that should be used to resolve modules.
-// *
-// * See: http://webpack.github.io/docs/configuration.html#resolve-extensions
-// */
-// extensions: ['.ts', '.js'],
-
-// /**
-// * Make sure root is Client
-// */
-// modules: [path.resolve(__dirname, 'Client'), 'node_modules']
-
-// },
-
-// /**
-// * Options affecting the normal modules.
-// *
-// * See: http://webpack.github.io/docs/configuration.html#module
-// */
-// module: {
-
-// rules: [
-
-// /**
-// * Tslint loader support for *.ts files
-// *
-// * See: https://github.com/wbuchwalter/tslint-loader
-// */
-// {
-// enforce: 'pre',
-// test: /\.ts$/,
-// loader: 'tslint-loader',
-// exclude: [helpers.root('node_modules')]
-// },
-
-// /**
-// * Source map loader support for *.js files
-// * Extracts SourceMaps for source files that as added as sourceMappingURL comment.
-// *
-// * See: https://github.com/webpack/source-map-loader
-// */
-// {
-// enforce: 'pre',
-// test: /\.js$/,
-// loader: 'source-map-loader',
-// exclude: [
-// // these packages have problems with their sourcemaps
-// helpers.root('node_modules/rxjs'),
-// helpers.root('node_modules/@angular')
-// ]
-// },
-
-// /**
-// * Typescript loader support for .ts and Angular 2 async routes via .async.ts
-// *
-// * See: https://github.com/s-panferov/awesome-typescript-loader
-// */
-// {
-// test: /\.ts$/,
-// loader: 'awesome-typescript-loader',
-// query: {
-// // use inline sourcemaps for "karma-remap-coverage" reporter
-// sourceMap: false,
-// inlineSourceMap: true,
-// compilerOptions: {
-
-// // Remove TypeScript helpers to be injected
-// // below by DefinePlugin
-// removeComments: true
-
-// }
-// },
-// exclude: [/\.e2e\.ts$/]
-// },
-
-// /**
-// * Json loader support for *.json files.
-// *
-// * See: https://github.com/webpack/json-loader
-// */
-// {
-// test: /\.json$/,
-// loader: 'json-loader',
-// exclude: [helpers.root('Client/index.html')]
-// },
-
-// /**
-// * Raw loader support for *.css files
-// * Returns file content as string
-// *
-// * See: https://github.com/webpack/raw-loader
-// */
-// {
-// test: /\.css$/,
-// loaders: ['to-string-loader', 'css-loader'],
-// exclude: [helpers.root('Client/index.html')]
-// },
-
-// /**
-// * Raw loader support for *.html
-// * Returns file content as string
-// *
-// * See: https://github.com/webpack/raw-loader
-// */
-// {
-// test: /\.html$/,
-// loader: 'raw-loader',
-// exclude: [helpers.root('Client/index.html')]
-// },
-
-// /**
-// * Instruments JS files with Istanbul for subsequent code coverage reporting.
-// * Instrument only testing sources.
-// *
-// * See: https://github.com/deepsweet/istanbul-instrumenter-loader
-// */
-// {
-// enforce: 'post',
-// test: /\.(js|ts)$/,
-// loader: 'istanbul-instrumenter-loader',
-// include: helpers.root('Client'),
-// exclude: [
-// /\.(e2e|spec)\.ts$/,
-// /node_modules/
-// ]
-// }
-
-// ]
-// },
-
-// /**
-// * Add additional plugins to the compiler.
-// *
-// * See: http://webpack.github.io/docs/configuration.html#plugins
-// */
-// plugins: [
-
-// /**
-// * Plugin: DefinePlugin
-// * Description: Define free variables.
-// * Useful for having development builds with debug logging or adding global constants.
-// *
-// * Environment helpers
-// *
-// * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
-// */
-// // NOTE: when adding more properties make sure you include them in custom-typings.d.ts
-// new DefinePlugin({
-// 'ENV': JSON.stringify(ENV),
-// 'HMR': false,
-// 'process.env': {
-// 'ENV': JSON.stringify(ENV),
-// 'NODE_ENV': JSON.stringify(ENV),
-// 'HMR': false,
-// }
-// }),
-
-// /**
-// * Plugin: ContextReplacementPlugin
-// * Description: Provides context to Angular's use of System.import
-// *
-// * See: https://webpack.github.io/docs/list-of-plugins.html#contextreplacementplugin
-// * See: https://github.com/angular/angular/issues/11580
-// */
-// new ContextReplacementPlugin(
-// // The (\\|\/) piece accounts for path separators in *nix and Windows
-// /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
-// helpers.root('src') // location of your src
-// ),
-
-// /**
-// * Plugin LoaderOptionsPlugin (experimental)
-// *
-// * See: https://gist.github.com/sokra/27b24881210b56bbaff7
-// */
-// new LoaderOptionsPlugin({
-// debug: true,
-// options: {
-
-// /**
-// * Static analysis linter for TypeScript advanced options configuration
-// * Description: An extensible linter for the TypeScript language.
-// *
-// * See: https://github.com/wbuchwalter/tslint-loader
-// */
-// tslint: {
-// emitErrors: false,
-// failOnHint: false,
-// resourcePath: 'src'
-// },
-
-// }
-// }),
-
-// ],
-
-// /**
-// * Include polyfills or mocks for various node stuff
-// * Description: Node configuration
-// *
-// * See: https://webpack.github.io/docs/configuration.html#node
-// */
-// node: {
-// global: true,
-// process: false,
-// crypto: 'empty',
-// module: false,
-// clearImmediate: false,
-// setImmediate: false
-// }
-
-// };
-// }
diff --git a/webpack.config.test.vendor.js b/webpack.config.test.vendor.js
deleted file mode 100644
index dcb3f274..00000000
--- a/webpack.config.test.vendor.js
+++ /dev/null
@@ -1,64 +0,0 @@
-var path = require('path');
-var webpack = require('webpack');
-
-var ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin');
-var ExtractTextPlugin = require('extract-text-webpack-plugin');
-var extractCSS = new ExtractTextPlugin('vendor.css');
-
-var isDevelopment = process.env.ASPNETCORE_ENVIRONMENT === 'Production' ? false : true;
-
-module.exports = {
- resolve: {
- extensions: ['.js']
- },
- module: {
- loaders: [
- { test: /\.(png|woff|woff2|eot|ttf|svg)$/, loader: 'url-loader?limit=100000' },
- { test: /\.css/, loader: extractCSS.extract(['css']) },
- // JSON files
- { test: /\.json$/, loader: 'json-loader' }
- ]
- },
- entry: {
- vendor: [
- '@angular/common',
- '@angular/compiler',
- '@angular/core',
- '@angular/http',
- '@angular/platform-browser',
- '@angular/platform-browser-dynamic',
- '@angular/router',
- '@angular/platform-server',
- 'angular2-universal',
- 'angular2-universal-polyfills',
- 'es6-shim',
- 'es6-promise',
- 'zone.js',
- ]
- },
- output: {
- path: path.join(__dirname, 'wwwroot', 'dist'),
- filename: '[name].js',
- library: '[name]_[hash]',
- },
- plugins: [
- new ContextReplacementPlugin(
- // The (\\|\/) piece accounts for path separators in *nix and Windows
- /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
- root('./Client')
- ),
- extractCSS,
- //new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
- new webpack.DllPlugin({
- path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
- name: '[name]_[hash]'
- })
- ].concat(isDevelopment ? [] : [
- new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } })
- ])
-};
-
-function root(args) {
- args = Array.prototype.slice.call(arguments, 0);
- return path.join.apply(path, [__dirname].concat(args));
-}
\ No newline at end of file
diff --git a/webpack.config.vendor.js b/webpack.config.vendor.js
index 1f79e402..775910fb 100644
--- a/webpack.config.vendor.js
+++ b/webpack.config.vendor.js
@@ -1,121 +1,162 @@
const path = require('path');
const webpack = require('webpack');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const merge = require('webpack-merge');
+const TerserPlugin = require('terser-webpack-plugin');
+const treeShakableModules = [
+ '@angular/animations',
+ '@angular/common',
+ '@angular/compiler',
+ '@angular/core',
+ '@angular/forms',
+ '@angular/http',
+ '@angular/platform-browser',
+ '@angular/platform-browser-dynamic',
+ '@angular/router',
+ 'ngx-bootstrap',
+ 'zone.js',
+];
+const nonTreeShakableModules = [
+ // 'bootstrap',
+ // 'bootstrap/dist/css/bootstrap.css',
+ 'core-js',
+ // 'es6-promise',
+ // 'es6-shim',
+ 'event-source-polyfill',
+ // 'jquery',
+];
-const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin');
-const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const allModules = treeShakableModules.concat(nonTreeShakableModules);
module.exports = (env) => {
-
- const extractCSS = new ExtractTextPlugin('vendor.css');
- const isDevBuild = !(env && env.prod);
-
- console.log(isDevBuild);
-
- const sharedConfig = {
- stats: { modules: false },
-
- resolve: {
- extensions: ['.js']
- },
- module: {
- loaders: [
- {
- test: /\.css/,
- loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader' })
- },
- { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' }
-
- ]
- },
- entry: {
- vendor: [
- '@angular/common',
- '@angular/compiler',
- '@angular/core',
- '@angular/http',
- '@angular/platform-browser',
- '@angular/platform-browser-dynamic',
- '@angular/router',
- '@angular/platform-server',
- 'angular2-universal',
- 'angular2-universal-polyfills',
- 'core-js',
- 'es6-promise',
- 'zone.js',
- //Added JS Libraries here
- 'jquery',
- 'signalr',
- //Added CSS Libraries here
- // './node_modules/bootstrap/dist/css/bootstrap.css',
- // './node_modules/font-awesome/css/font-awesome.css'
- ]
- },
- output: {
- // path: path.join(__dirname, 'wwwroot', 'dist'),
- filename: '[name].js',
- library: '[name]_[hash]',
- publicPath: '/dist/'
- },
- plugins: [
- // Uncomment if you want to use jQuery
- // new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
-
- new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './Client')), // Workaround for https://github.com/angular/angular/issues/11580
- // extractCSS,
- //new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
- // new webpack.DllPlugin({
- // path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
- // name: '[name]_[hash]'
- // }),
- new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
+ console.log(`env = ${JSON.stringify(env)}`)
+ const extractCSS = new MiniCssExtractPlugin({
+ // Options similar to the same options in webpackOptions.output
+ // both options are optional
+ filename: "[name].css",
+ chunkFilename: "[id].css"
+ });
+ const isDevBuild = !(env && env.prod);
+ const sharedConfig = {
+ mode: isDevBuild ? "development" : "production",
+ stats: {
+ modules: false
+ },
+ resolve: {
+ extensions: ['.js']
+ },
+ module: {
+ rules: [{
+ test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/,
+ use: 'url-loader?limit=100000'
+ }]
+ },
+ output: {
+ publicPath: 'dist/',
+ filename: '[name].js',
+ library: '[name]_[hash]'
+ },
+ plugins: [
+ // new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
+ new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580
+ new webpack.ContextReplacementPlugin(/(.+)?angular(\\|\/)core(.+)?/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/14898
+ new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
+ ]
+ };
+ const clientBundleConfig = merge(sharedConfig, {
+ entry: {
+ // To keep development builds fast, include all vendor dependencies in the vendor bundle.
+ // But for production builds, leave the tree-shakable ones out so the AOT compiler can produce a smaller bundle.
+ vendor: isDevBuild ? allModules : nonTreeShakableModules
+ },
+ output: {
+ path: path.join(__dirname, 'wwwroot', 'dist')
+ },
+ module: {
+ rules: [{
+ test: /\.css(\?|$)/,
+ use: [
+ MiniCssExtractPlugin.loader,
+ isDevBuild ? 'css-loader' : 'css-loader?minimize'
]
- // .concat(isDevBuild ? [] : [
- // new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } })
- // ])
- };
+ }]
+ },
+ plugins: [
+ new MiniCssExtractPlugin({
+ filename: 'vendor.css',
+ }),
+ new webpack.DllPlugin({
+ path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
+ name: '[name]_[hash]'
+ })
+ ].concat(isDevBuild ? [] : [
- const clientConfig = merge(sharedConfig, {
- output: { path: path.join(__dirname, 'wwwroot', 'dist') },
- module: {
- rules: [
- { test: /\.css(\?|$)/, use: extractCSS.extract({ loader: 'css-loader' }) }
- ]
- },
- plugins: [
- extractCSS,
- new webpack.DllPlugin({
- path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
- name: '[name]_[hash]'
- })
- ].concat(isDevBuild ? [] : [
- new webpack.optimize.UglifyJsPlugin()
- ])
- });
+ ]),
+ optimization: {
+ minimizer: [].concat(isDevBuild ? [] : [
+ // we specify a custom TerserPlugin here to get source maps in production
+ new TerserPlugin({
+ cache: true,
+ parallel: true,
+ sourceMap: true,
+ terserOptions: {
+ compress: false,
+ ecma: 6,
+ mangle: true,
+ keep_classnames: true,
+ keep_fnames: true,
+ },
+ })
+ ])
+ }
+ });
- const serverConfig = merge(sharedConfig, {
- target: 'node',
- resolve: { mainFields: ['main'] },
- output: {
- path: path.join(__dirname, 'Client', 'dist'),
- libraryTarget: 'commonjs2',
- },
- module: {
- rules: [ { test: /\.css(\?|$)/, use: ['to-string-loader', 'css-loader'] } ]
- },
- entry: { vendor: ['aspnet-prerendering'] },
- plugins: [
- new webpack.DllPlugin({
- path: path.join(__dirname, 'Client', 'dist', '[name]-manifest.json'),
- name: '[name]_[hash]'
- })
+ const serverBundleConfig = merge(sharedConfig, {
+ target: 'node',
+ resolve: {
+ mainFields: ['main']
+ },
+ entry: {
+ vendor: allModules.concat(['aspnet-prerendering'])
+ },
+ output: {
+ path: path.join(__dirname, 'ClientApp', 'dist'),
+ libraryTarget: 'commonjs2',
+ },
+ module: {
+ rules: [{
+ test: /\.css(\?|$)/,
+ use: [
+ MiniCssExtractPlugin.loader,
+ isDevBuild ? 'css-loader' : 'css-loader?minimize'
]
- });
-
- return [
- clientConfig,
- serverConfig
- ];
+ }]
+ },
+ plugins: [
+ new webpack.DllPlugin({
+ path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'),
+ name: '[name]_[hash]'
+ })
+ ].concat(isDevBuild ? [] : []),
+ optimization: {
+ minimizer: [].concat(isDevBuild ? [] : [
+ // we specify a custom TerserPlugin here to get source maps in production
+ new TerserPlugin({
+ cache: true,
+ parallel: true,
+ sourceMap: true,
+ terserOptions: {
+ compress: false,
+ ecma: 6,
+ mangle: true,
+ keep_classnames: true,
+ keep_fnames: true,
+ },
+ })
+ ])
+ }
+ });
-};
+ return [clientBundleConfig, serverBundleConfig];
+}
diff --git a/wwwroot/assets/i18n/en.json b/wwwroot/assets/i18n/en.json
new file mode 100644
index 00000000..c6526f71
--- /dev/null
+++ b/wwwroot/assets/i18n/en.json
@@ -0,0 +1,9 @@
+{
+ "HOME": "Home",
+ "HOME_FEATURE_LIST_TITLE": "What does this Starter offer?",
+ "HOME_ISSUES_TITLE": "Having issues?",
+ "COUNTER": "Counter",
+ "SWITCH_LANGUAGE": "Switch language",
+ "ENGLISH": "English",
+ "NORWEGIAN": "Norwegian"
+}
diff --git a/wwwroot/assets/i18n/no.json b/wwwroot/assets/i18n/no.json
new file mode 100644
index 00000000..87728ccf
--- /dev/null
+++ b/wwwroot/assets/i18n/no.json
@@ -0,0 +1,9 @@
+{
+ "HOME": "Hjem",
+ "HOME_FEATURE_LIST_TITLE": "Hva tilbyr dette prosjektet?",
+ "HOME_ISSUES_TITLE": "Oppever du problemer?",
+ "COUNTER": "Teller",
+ "SWITCH_LANGUAGE": "Bytt språk",
+ "ENGLISH": "Engelsk",
+ "NORWEGIAN": "Norsk"
+}
diff --git a/wwwroot/favicon.ico b/wwwroot/favicon.ico
index 76cd0ece..23015bba 100644
Binary files a/wwwroot/favicon.ico and b/wwwroot/favicon.ico differ
diff --git a/wwwroot/images/icon144x144.png b/wwwroot/images/icon144x144.png
new file mode 100644
index 00000000..b53f3791
Binary files /dev/null and b/wwwroot/images/icon144x144.png differ
diff --git a/wwwroot/images/icon192x192.png b/wwwroot/images/icon192x192.png
new file mode 100644
index 00000000..6f4b36a1
Binary files /dev/null and b/wwwroot/images/icon192x192.png differ
diff --git a/wwwroot/images/icon256x256.png b/wwwroot/images/icon256x256.png
new file mode 100644
index 00000000..66c420db
Binary files /dev/null and b/wwwroot/images/icon256x256.png differ
diff --git a/wwwroot/images/icon36x36.png b/wwwroot/images/icon36x36.png
new file mode 100644
index 00000000..974921f1
Binary files /dev/null and b/wwwroot/images/icon36x36.png differ
diff --git a/wwwroot/images/icon384x384.png b/wwwroot/images/icon384x384.png
new file mode 100644
index 00000000..56c8c739
Binary files /dev/null and b/wwwroot/images/icon384x384.png differ
diff --git a/wwwroot/images/icon48x48.png b/wwwroot/images/icon48x48.png
new file mode 100644
index 00000000..734be054
Binary files /dev/null and b/wwwroot/images/icon48x48.png differ
diff --git a/wwwroot/images/icon512x512.png b/wwwroot/images/icon512x512.png
new file mode 100644
index 00000000..ed0b48d3
Binary files /dev/null and b/wwwroot/images/icon512x512.png differ
diff --git a/wwwroot/images/icon72x72.png b/wwwroot/images/icon72x72.png
new file mode 100644
index 00000000..77fde9eb
Binary files /dev/null and b/wwwroot/images/icon72x72.png differ
diff --git a/wwwroot/images/icon96x96.png b/wwwroot/images/icon96x96.png
new file mode 100644
index 00000000..c5a86651
Binary files /dev/null and b/wwwroot/images/icon96x96.png differ
diff --git a/wwwroot/manifest.json b/wwwroot/manifest.json
new file mode 100644
index 00000000..b386fa18
--- /dev/null
+++ b/wwwroot/manifest.json
@@ -0,0 +1,49 @@
+{
+ "name": "ASP.NET Core 2.1 & Angular 7(+) Advanced Starter",
+ "short_name": "aspnetcore-angular2-universal",
+ "description":
+ "ASP.NET Core 2.1 & Angular 7(+) Advanced Starter - with Server-side prerendering (for Angular SEO)!",
+ "icons": [
+ {
+ "src": "/images/icon36x36.png",
+ "sizes": "36x36",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon48x48.png",
+ "sizes": "48x48",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon72x72.png",
+ "sizes": "72x72",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon96x96.png",
+ "sizes": "96x96",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon144x144.png",
+ "sizes": "144x144",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon256x256.png",
+ "sizes": "256x256",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon384x384.png",
+ "sizes": "384x384",
+ "type": "image/png"
+ }, {
+ "src": "/images/icon512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "background_color": "#3E4EB8",
+ "theme_color": "#2F3BA2",
+ "display": "standalone",
+ "start_url": "/"
+}
diff --git a/wwwroot/web.config b/wwwroot/web.config
deleted file mode 100644
index 30663954..00000000
--- a/wwwroot/web.config
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-