pull/3398/head
Alper Ebicoglu 6 years ago
commit d513d2a111

@ -1,10 +1,10 @@
# Component Replacement
## Component Replacement
You can replace some ABP components with your custom components.
The reason that you **can replace** but **cannot customize** default ABP components is disabling or changing a part of that component can cause problems. So we named those components as _Replaceable Components_.
## How to Replace a Component
### How to Replace a Component
Create a new component that you want to use instead of an ABP component. Add that component to `declarations` and `entryComponents` in the `AppModule`.
@ -29,7 +29,54 @@ export class AppComponent {
![Example Usage](./images/component-replacement.gif)
## Available Replaceable Components
### How to Replace a Layout
Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced with the same way.
> A layout component template should contain `<router-outlet></router-outlet>` element.
The below example describes how to replace the `ApplicationLayoutComponent`:
Run the following command to generate a layout in `angular` folder:
```bash
yarn ng generate component shared/my-application-layout --export --entryComponent
# You don't need the --entryComponent option in Angular 9
```
Add the following code in your layout template (`my-layout.component.html`) where you want the page to be loaded.
```html
<router-outlet></router-outlet>
```
Open the `app.component.ts` and add the below content:
```js
import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent
import { MyApplicationLayoutComponent } from './shared/my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent
import { Store } from '@ngxs/store'; // imported Store
//...
export class AppComponent {
constructor(..., private store: Store) {} // injected Store
ngOnInit() {
// added below content
this.store.dispatch(
new AddReplaceableComponent({
component: MyApplicationLayoutComponent,
key: 'Theme.ApplicationLayoutComponent',
}),
);
//...
}
}
```
### Available Replaceable Components
| Component key | Description |
| -------------------------------------------------- | --------------------------------------------- |

@ -25,9 +25,9 @@ The files generated with the `--module all` option like below:
### Services
Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Http-Requests.md#restservice).
Each generated service matches a back-end controller. The services methods call back-end APIs via [RestService](./Http-Requests#restservice).
A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests.md#how-to-get-a-specific-api-endpoint-from-application-config)
A variable named `apiName` (available as of v2.4) is defined in each service. `apiName` matches the module's RemoteServiceName. This variable passes to the `RestService` as a parameter at each request. If there is no microservice API defined in the environment, `RestService` uses the default. See [getting a specific API endpoint from application config](./Http-Requests#how-to-get-a-specific-api-endpoint-from-application-config)
The `providedIn` property of the services is defined as `'root'`. Therefore no need to add a service as a provider to a module. You can use a service by injecting it into a constructor as shown below:
@ -64,4 +64,4 @@ Initial values can optionally be passed to each class constructor.
## What's Next?
* [HTTP Requests](./Http-Requests.md)
* [HTTP Requests](./Http-Requests)

@ -68,7 +68,7 @@
}
<div class="for-mobile">
<div class="navbar-light">
<div class="navbar-dark">
<button type="button" class="open-dmenu navbar-toggler" aria-label="Close">
<span class="navbar-toggler-icon"></span>
</button>
@ -108,7 +108,7 @@
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-check-square-o" aria-hidden="true" data-toggle="tooltip" title="@L["Version"]"></i>
<i class="fas fa-code-branch" aria-hidden="true" data-toggle="tooltip" title="@L["Version"]"></i>
</label>
</div>
@ -124,15 +124,15 @@
@if (Model.LanguageSelectListItems.Count > 1)
{
<div class="col @(Model.VersionSelectItems.Any()?"pl-0":"")">
<div class="col-5 @(Model.VersionSelectItems.Any()?"pl-0":"")">
<div class="docs-version docs-language @(Model.VersionSelectItems.Any()?"pl-1":"")">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
@*<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-globe" aria-hidden="true" data-toggle="tooltip" title="@L["Language"]"></i>
</label>
</div>
</div>*@
<select asp-items="Model.LanguageSelectListItems"
class="form-control"
onchange="window.location.replace(this.value)">
@ -163,11 +163,11 @@
@if (Model.FullSearchEnabled)
{
<div class="docs-version mb-4">
<div class="docs-version mt-2 mb-4">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text"><i class="fa fa-filter"></i></label>
<label class="input-group-text"><i class="fa fa-search"></i></label>
</div>
<input class="form-control"
@ -224,7 +224,9 @@
@if (!string.IsNullOrEmpty(Model.Document.EditLink))
{
<a href="@Model.Document.EditLink" target="_blank">
<i class="fa fa-edit"></i> @(L["Edit"]) (@L["LastEditTime"]: @Model.Document.LastUpdatedTime.ToShortDateString())
<i class="fa fa-edit"></i>
@(L["Edit"])
<span class="for-desktop">(@L["LastEditTime"]: @Model.Document.LastUpdatedTime.ToShortDateString())</span>
</a>
}
</div>
@ -232,20 +234,21 @@
<div class="float-right mr-3">
@if (Model.Document.Contributors != null && Model.Document.Contributors.Count > 0)
{
@(L["Contributors"].Value + " :")
<span class="for-desktop">
@(L["Contributors"].Value + " :") </span>
@foreach (var contributor in Model.Document.Contributors)
{
<a href="@contributor.UserProfileUrl" target="_blank">
<img src="@contributor.AvatarUrl"
class="rounded-circle"
class="rounded-circle"
alt="Avatar"
height="21"
width="21"
height="21"
width="21"
title="@contributor.Username" />
</a>
}
}
</div>
}
</div>
</div>

@ -15,53 +15,73 @@
<style>
highlight {
font-weight: bold;
color: red;
font-style: italic;
color: #212529;
background: #f9efa6;
padding: 0 3px;
border-radius: 3px;
}
body {
background-color: rgba(0, 0, 0, 0.03);
}
</style>
}
<div class="container">
<div class="container mb-5">
<form method="get" action="/search/@Model.LanguageCode/@Model.ProjectName/@Model.Version/" class="mt-4">
<input type="text" asp-for="@Model.KeyWord" class="form-control" />
<button type="submit" class="btn-block btn-primary btn-lg mt-3">Search</button>
<h4 class="text-center my-3">Search in Documents</h4>
<div class="card rounded">
<div class="p-3 bg-white">
<div class="form-row">
<div class="col"><input type="text" asp-for="@Model.KeyWord" class="form-control" /></div>
<div class="col-auto"><button type="submit" class="btn btn-primary px-md-5">Search</button></div>
</div>
</div>
</div>
</form>
<div class="my-3 p-3 bg-white rounded">
<h6 class="border-bottom pb-4 mb-0">Search results</h6>
@foreach (var docs in Model.SearchOutputs)
{
<div class="media text-muted pt-3">
<div class="media-body pb-3 mb-0 small border-bottom">
<div class="list-group">
<div class="card mt-4 rounded">
<div class="card-header">
<h5 class="text-center ">Search Results</h5>
</div>
<div class="p-5 card-body">
@foreach (var docs in Model.SearchOutputs)
{
<div class="media text-muted">
<div class="media-body pb-3 small">
<div class="list-group">
@functions
{
string RemoveFileExtensionFromPath(string path)
{
if (path == null)
@functions
{
return null;
}
string RemoveFileExtensionFromPath(string path)
{
if (path == null)
{
return null;
}
return path.EndsWith("." + @Model.Project.Format)
? path.Left(path.Length - Model.Project.Format.Length - 1)
: path;
return path.EndsWith("." + @Model.Project.Format)
? path.Left(path.Length - Model.Project.Format.Length - 1)
: path;
}
}
}
<a href="/@Model.LanguageCode/@Model.ProjectName/@Model.Version/@RemoveFileExtensionFromPath(docs.Name)">
<h3>@RemoveFileExtensionFromPath(docs.Name)</h3></a>
@foreach (var highlight in docs.Highlight)
{
<p class="list-group-item list-group-item-action">@Html.Raw(highlight)</p>
}
<h5 class="mb-3">
<a href="/@Model.LanguageCode/@Model.ProjectName/@Model.Version/@RemoveFileExtensionFromPath(docs.Name)">
@RemoveFileExtensionFromPath(docs.Name)
</a>
</h5>
<div class="mb-4">
@foreach (var highlight in docs.Highlight)
{
<p class=" ">@Html.Raw(highlight)</p>
}
</div>
</div>
</div>
</div>
</div>
}
}
</div>
</div>
</div>

@ -10,21 +10,21 @@
transition: color .25s linear;
color: gray; }
.docs-page .anchorjs-link:hover {
color: #007bff;
text-decoration: none; }
.docs-page .docs-sidebar {
background: #f5f7f9;
padding-right: 1rem;
position: relative;
top: 0px;
left: 0;
position: fixed; }
position: fixed;
background: #1d1d1d; }
.docs-page .docs-sidebar .input-group {
border-radius: 5px;
overflow: hidden; }
.docs-page .docs-sidebar .docs-sidebar-wrapper {
width: 270px;
float: right; }
.docs-page .docs-sidebar .docs-sidebar-wrapper input.form-control {
background: none;
background: #fff;
border: 0; }
.docs-page .docs-sidebar .docs-sidebar-wrapper input.form-control:focus, .docs-page .docs-sidebar .docs-sidebar-wrapper input.form-control:active, .docs-page .docs-sidebar .docs-sidebar-wrapper input.form-control:hover, .docs-page .docs-sidebar .docs-sidebar-wrapper input.form-control:visited {
box-shadow: none; }
@ -33,27 +33,28 @@
padding: 0 1rem;
margin: .25rem 0; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select {
border-radius: 3px;
border: 1px solid #e9ecef; }
border-radius: 3px; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select .input-group-text {
padding: 0.375rem 0.6rem;
padding: 0 10px;
font-size: .9rem;
width: 32px;
height: 30px;
width: 26px;
height: 34px;
line-height: 1;
border-radius: 0px;
border: 1px solid #e9ecef; }
border: 0; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select .input-group-text i {
color: #666; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control {
padding: 0 10px;
padding: 0 10px 2px 10px;
border: 0;
min-height: 30px;
height: 30px;
font-size: .85em;
border-radius: 1px; }
min-height: 34px;
height: 34px;
font-size: .9em;
border-radius: 0px; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control:focus, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control:active, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control:hover, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control:visited, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control:focus, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control:active, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control:hover, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control:visited {
box-shadow: none; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control {
padding-left: 6px; }
padding: 0 10px 2px 6px; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-filter {
padding: 0 1rem;
margin: .5rem 0;
@ -64,7 +65,7 @@
height: 100vh; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list > ul {
display: block;
height: calc(100vh - 220px);
height: calc(100vh - 320px);
overflow-y: auto; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul {
font-size: .935em;
@ -88,6 +89,12 @@
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a.last-link {
top: 11px;
color: #aaa; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li .badge {
text-transform: uppercase;
font-size: 9px;
position: relative;
letter-spacing: .125px;
top: -2px; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li span.tree-toggle {
color: #999;
padding: 7px 0;
@ -129,10 +136,10 @@
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li.selected-tree.last-link > span .fa {
transform: rotate(0deg); }
.docs-page .docs-sidebar .docs-top .navbar-logo .navbar-brand {
font-size: 1.5rem;
font-size: 1.35rem;
color: #000;
font-weight: 700;
padding: 20px 0 10px;
padding: 15px 0 15px;
line-height: 1; }
.docs-page .docs-sidebar .docs-top .navbar-logo .navbar-brand strong {
font-weight: 300;
@ -153,7 +160,8 @@
.docs-page .docs-sidebar .docs-top .navbar-logo .navbar-logo-desc strong {
display: block; }
.docs-page .docs-content {
overflow-x: scroll; }
overflow-x: scroll;
min-height: 100vh; }
.docs-page .docs-content .contributors {
position: absolute;
top: 15px;
@ -222,10 +230,11 @@
.docs-page .docs-content article.docs-body .blockquote {
margin-bottom: 1rem;
margin-left: 0;
border-left: 2px solid gray;
padding: 1em;
background-color: #eee;
padding-bottom: .2em; }
border-left: 3px solid #d2dbe4;
padding: 1em 1.5em;
background-color: #e9edf1;
padding-bottom: .2em;
font-size: 1em; }
.docs-page .docs-content article.docs-body img {
max-width: 100%;
border: 1px solid #f4f5f7;
@ -257,10 +266,8 @@
.docs-page .docs-page-index .docs-inner-anchors {
position: fixed;
top: 0px;
/* max-width: 270px; */
padding: 10px;
font-size: .90em;
/* height: 100vh; */ }
font-size: .90em; }
.docs-page .docs-page-index .docs-inner-anchors .navbar .nav-pills {
font-size: .92em;
margin-left: 15px;
@ -300,83 +307,95 @@
.docs-page .docs-page-index .scroll-top-btn.showup {
display: block; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control, .docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control {
background: #000000;
color: white; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control {
background: #000000;
color: white; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control::placeholder {
color: white;
opacity: .5; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version .version-select label {
background: #000000;
border-color: #000000;
color: #ddd; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-filter .form-control {
background: #333;
color: #999; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-filter select {
border: 0;
border-radius: 6px; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-filter .filter-icon i.fa {
color: #aaa; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a {
color: #aaa;
border-bottom: 0; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a:hover {
color: #fff; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon {
font-size: .85em;
transition: .3s;
width: 18px;
height: 18px;
text-align: center;
padding: 0;
line-height: 1;
border-radius: 50%;
margin-right: 4px;
position: absolute;
left: 2px;
top: 11px;
color: #aaa;
cursor: default; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon .fa-long-arrow-right.no-link {
color: #555; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon .fa-chevron-right {
cursor: pointer; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon.last-link {
top: 11px; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li span.tree-toggle {
color: #555;
padding: 7px 0;
display: block;
border-bottom: 0; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li.selected-tree > a {
color: #fff;
transition: .4s; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li.selected-tree > a span .fa {
color: #fff; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list ul li.selected-tree > a span:not(.last-link) .fa {
transform: rotate(90deg);
color: #fff; }
.docs-page .docs-sidebar .docs-top .navbar-logo .navbar-brand {
color: #fff;
text-transform: uppercase;
white-space: unset; }
.docs-page .docs-sidebar .docs-top .navbar-logo .go-back-site {
color: #fff;
text-align: center;
display: block;
width: 100%;
background: #444;
padding: 6px 0 8px;
border-radius: 5px; }
.docs-page .docs-sidebar .docs-top .navbar-logo .navbar-logo-desc {
color: #ddd; }
@media (min-width: 1100px) {
.container {
max-width: 1080px; }
.docs-page .docs-sidebar.dark-sidebar {
background: #191919; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-version .version-select {
border: 1px solid #333; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-version .version-select select.form-control, .docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control {
background: #191919;
border-color: #191919;
color: #999; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control {
background: #191919;
border-color: #191919;
color: #999; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-version .version-select input.form-control::placeholder {
color: #999;
opacity: .5; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-version .version-select label {
background: #333;
border-color: #333;
color: #ddd; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-filter .form-control {
background: #333;
color: #999; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-filter select {
border: 0;
border-radius: 6px; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-filter .filter-icon i.fa {
color: #aaa; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a {
color: #aaa;
border-bottom: 0; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a:hover {
color: #fff; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon {
font-size: .85em;
transition: .3s;
width: 18px;
height: 18px;
text-align: center;
padding: 0;
line-height: 1;
border-radius: 50%;
margin-right: 4px;
position: absolute;
left: 2px;
top: 11px;
color: #aaa;
cursor: default; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon .fa-long-arrow-right.no-link {
color: #555; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon .fa-chevron-right {
cursor: pointer; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li a .plus-icon.last-link {
top: 11px; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li span.tree-toggle {
color: #555;
padding: 7px 0;
display: block;
border-bottom: 0; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li.selected-tree > a {
color: #fff;
transition: .4s; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li.selected-tree > a span .fa {
color: #fff; }
.docs-page .docs-sidebar.dark-sidebar .docs-sidebar-wrapper .docs-tree-list ul li.selected-tree > a span:not(.last-link) .fa {
transform: rotate(90deg);
color: #fff; }
.docs-page .docs-sidebar.dark-sidebar .docs-top .navbar-logo .navbar-brand {
color: #fff;
text-transform: uppercase;
white-space: unset; }
.docs-page .docs-sidebar.dark-sidebar .docs-top .navbar-logo .go-back-site {
color: #fff; }
.docs-page .docs-sidebar.dark-sidebar .docs-top .navbar-logo .navbar-logo-desc {
color: #ddd; } }
max-width: 1080px; } }
@media (min-width: 1366px) {
.container {
@ -420,30 +439,21 @@
left: 0;
width: 100%;
z-index: 100;
background: #f5f7f9;
background: #1d1d1d;
display: none; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-tree-list .docs-filter {
padding: 0 0 1rem !important; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version label {
width: 36px;
text-align: center;
padding-left: 0;
padding-right: 0;
display: inline-block;
line-height: 26px; }
.docs-page .docs-sidebar .docs-sidebar-wrapper .docs-version input.form-control {
padding-left: 12px !important; }
.docs-page .docs-sidebar .docs-top .navbar-logo {
padding: 0rem;
padding-top: .3rem;
display: block;
text-align: center; }
.docs-page .docs-sidebar .docs-top .navbar-logo .navbar-brand {
font-size: 1.5rem;
font-size: 1.25rem;
font-weight: 700;
display: block;
margin-right: 0em;
padding: 7px 0 10px;
padding: 10px 0 15px;
text-transform: uppercase; }
.docs-page .docs-sidebar .docs-top .navbar-logo .navbar-brand .docs-logo {
width: 110px; }

File diff suppressed because one or more lines are too long

@ -16,19 +16,20 @@ body {
}
.anchorjs-link:hover {
color: #007bff;
text-decoration: none;
}
.docs-sidebar {
background: #f5f7f9;
padding-right: 1rem;
position: relative;
top: 0px;
left: 0;
position: fixed;
.docs-language {
background: #1d1d1d;
.input-group {
border-radius: 5px;
overflow: hidden;
}
.docs-sidebar-wrapper {
@ -36,8 +37,6 @@ body {
float: right;
input.form-control {
background: none;
background: #fff;
border: 0;
&:focus, &:active, &:hover, &:visited {
@ -52,25 +51,28 @@ body {
.version-select {
border-radius: 3px;
border: 1px solid #e9ecef;
.input-group-text {
padding: 0.375rem 0.6rem;
padding: 0 10px;
font-size: .9rem;
width: 32px;
height: 30px;
width: 26px;
height: 34px;
line-height: 1;
border-radius: 0px;
border: 1px solid #e9ecef;
border: 0;
i {
color: #666;
}
}
select.form-control, input.form-control {
padding: 0 10px;
padding: 0 10px 2px 10px;
border: 0;
min-height: 30px;
height: 30px;
font-size: .85em;
border-radius: 1px;
min-height: 34px;
height: 34px;
font-size: .9em;
border-radius: 0px;
&:focus, &:active, &:hover, &:visited {
box-shadow: none;
@ -78,10 +80,7 @@ body {
}
select.form-control {
padding-left: 6px;
}
label {
padding: 0 10px 2px 6px;
}
}
}
@ -104,7 +103,7 @@ body {
> ul {
display: block;
height: calc(100vh - 220px);
height: calc(100vh - 320px);
overflow-y: auto;
}
@ -139,6 +138,14 @@ body {
}
}
.badge {
text-transform: uppercase;
font-size: 9px;
position: relative;
letter-spacing: .125px;
top: -2px;
}
span.tree-toggle {
color: #999;
padding: 7px 0;
@ -224,16 +231,16 @@ body {
.docs-top {
.navbar-logo {
.navbar-brand {
font-size: 1.5rem;
font-size: 1.35rem;
color: #000;
font-weight: 700;
padding: 20px 0 10px;
padding: 15px 0 15px;
line-height: 1;
strong {
font-weight: 300;
text-transform: uppercase;
font-size: .7em;
font-size: .7em;
letter-spacing: 1px;
}
}
@ -264,6 +271,7 @@ body {
.docs-content {
overflow-x: scroll;
min-height: 100vh;
.contributors {
position: absolute;
@ -372,10 +380,11 @@ body {
.blockquote {
margin-bottom: 1rem;
margin-left: 0;
border-left: 2px solid gray;
padding: 1em;
background-color: #eee;
padding-bottom: .2em
border-left: 3px solid #d2dbe4;
padding: 1em 1.5em;
background-color: #e9edf1;
padding-bottom: .2em;
font-size: 1em;
}
img {
@ -433,10 +442,9 @@ body {
.docs-inner-anchors {
position: fixed;
top: 0px;
/* max-width: 270px; */
padding: 10px;
font-size: .90em;
/* height: 100vh; */
.navbar {
.nav-pills {
font-size: .92em;
@ -508,163 +516,189 @@ body {
}
@media (min-width: 1100px) {
.container {
max-width: 1080px;
}
.docs-page {
.docs-sidebar {
&.dark-sidebar {
background: #191919;
.docs-sidebar-wrapper {
.docs-version {
.version-select {
border: 1px solid #333;
.docs-page {
.docs-sidebar {
.docs-sidebar-wrapper {
select.form-control, input.form-control {
background: #191919;
border-color: #191919;
color: #999;
}
.docs-version {
.version-select {
input.form-control {
background: #191919;
border-color: #191919;
color: #999;
select.form-control, input.form-control {
background: #000000;
color: white;
}
&::placeholder {
color: #999;
opacity: .5;
}
}
input.form-control {
background: #000000;
color: white;
label {
background: #333;
border-color: #333;
color: #ddd;
}
&::placeholder {
color: white;
opacity: .5;
}
}
.docs-filter {
.form-control {
background: #333;
color: #999;
}
label {
background: #000000;
border-color: #000000;
color: #ddd;
}
}
}
select {
border: 0;
border-radius: 6px;
}
.docs-filter {
.form-control {
background: #333;
color: #999;
}
.filter-icon {
i.fa {
color: #aaa;
}
}
}
select {
border: 0;
border-radius: 6px;
}
.docs-tree-list {
ul {
li {
.filter-icon {
i.fa {
color: #aaa;
}
}
}
.docs-tree-list {
ul {
li {
a {
color: #aaa;
border-bottom: 0;
&:hover {
color: #fff;
}
a {
color: #aaa;
border-bottom: 0;
.plus-icon {
font-size: .85em;
transition: .3s;
width: 18px;
height: 18px;
text-align: center;
padding: 0;
line-height: 1;
border-radius: 50%;
margin-right: 4px;
position: absolute;
left: 2px;
top: 11px;
color: #aaa;
cursor: default;
.fa-long-arrow-right {
&.no-link {
color: #555;
}
}
&:hover {
color: #fff;
}
.fa-chevron-right {
cursor: pointer;
}
.plus-icon {
font-size: .85em;
transition: .3s;
width: 18px;
height: 18px;
text-align: center;
padding: 0;
line-height: 1;
border-radius: 50%;
margin-right: 4px;
position: absolute;
left: 2px;
top: 11px;
color: #aaa;
cursor: default;
&.last-link {
top: 11px;
}
.fa-long-arrow-right {
&.no-link {
color: #555;
}
}
.fa-chevron-right {
cursor: pointer;
}
span.tree-toggle {
color: #555;
padding: 7px 0;
display: block;
border-bottom: 0;
&.last-link {
top: 11px;
}
}
}
&.selected-tree {
> a {
color: #fff;
transition: .4s;
span.tree-toggle {
color: #555;
padding: 7px 0;
display: block;
border-bottom: 0;
}
span {
.fa {
color: #fff;
}
&:not(.last-link) .fa {
transform: rotate(90deg);
color: #fff;
}
}
&.selected-tree {
> a {
color: #fff;
transition: .4s;
span {
.fa {
color: #fff;
}
&:not(.last-link) .fa {
transform: rotate(90deg);
color: #fff;
}
}
}
}
}
}
}
}
.docs-top {
.navbar-logo {
.navbar-brand {
color: #fff;
text-transform: uppercase;
white-space: unset;
}
.docs-top {
.navbar-logo {
.navbar-brand {
color: #fff;
text-transform: uppercase;
white-space: unset;
}
.go-back-site {
color: #fff;
}
.go-back-site {
color: #fff;
text-align: center;
display: block;
width: 100%;
background: #444;
padding: 6px 0 8px;
border-radius: 5px;
}
.navbar-logo-desc {
color: #ddd;
}
}
.navbar-logo-desc {
color: #ddd;
}
}
}
}
}
@media (min-width: 1100px) {
.container {
max-width: 1080px;
}
}
@media (min-width: 1366px) {
.container {
max-width: 1340px;
@ -720,7 +754,7 @@ body {
left: 0;
width: 100%;
z-index: 100;
background: #f5f7f9;
background: #1d1d1d;
display: none;
.docs-filter {
@ -732,17 +766,6 @@ body {
}
.docs-version {
label {
width: 36px;
text-align: center;
padding-left: 0;
padding-right: 0;
display: inline-block;
line-height: 26px;
}
input.form-control {
padding-left: 12px !important;
}
}
}
@ -754,11 +777,11 @@ body {
text-align: center;
.navbar-brand {
font-size: 1.5rem;
font-size: 1.25rem;
font-weight: 700;
display: block;
margin-right: 0em;
padding: 7px 0 10px;
padding: 10px 0 15px;
text-transform: uppercase;
.docs-logo {
@ -971,6 +994,7 @@ div.code-toolbar > .toolbar a {
background-color: #bddcfd;
border: 1px solid #bddcfd;
@media screen and (max-width: 1366px) {
display: none;
}
@ -1009,4 +1033,5 @@ div.code-toolbar > .toolbar a {
display: none;
}
}
}
}

@ -526,5 +526,5 @@
}
}
},
"defaultProject": "core"
"defaultProject": "dev-app"
}

@ -21,9 +21,6 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })];
imports: [
CoreModule.forRoot({
environment,
requirements: {
layouts: LAYOUTS,
},
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot({ redirectUrl: '/' }),

@ -1,12 +1,12 @@
import { Component, Input, OnDestroy, Type, Injector } from '@angular/core';
import { Component, OnDestroy, Type } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router, UrlSegment } from '@angular/router';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { Store } from '@ngxs/store';
import snq from 'snq';
import { eLayoutType } from '../enums/common';
import { Config } from '../models/config';
import { ABP } from '../models/common';
import { ReplaceableComponents } from '../models/replaceable-components';
import { ConfigState } from '../states/config.state';
import { ReplaceableComponentsState } from '../states/replaceable-components.state';
import { takeUntilDestroy } from '../utils/rxjs-utils';
@Component({
@ -20,24 +20,10 @@ import { takeUntilDestroy } from '../utils/rxjs-utils';
`,
})
export class DynamicLayoutComponent implements OnDestroy {
@Select(ConfigState.getOne('requirements')) requirements$: Observable<Config.Requirements>;
layout: Type<any>;
constructor(private router: Router, private route: ActivatedRoute, private store: Store) {
const {
requirements: { layouts },
routes,
} = this.store.selectSnapshot(ConfigState.getAll);
if ((this.route.snapshot.data || {}).layout) {
this.layout = layouts
.filter(l => !!l)
.find(
(l: any) =>
snq(() => l.type.toLowerCase().indexOf(this.route.snapshot.data.layout), -1) > -1,
);
}
const { routes } = this.store.selectSnapshot(ConfigState.getAll);
router.events.pipe(takeUntilDestroy(this)).subscribe(event => {
if (event instanceof NavigationEnd) {
@ -45,15 +31,24 @@ export class DynamicLayoutComponent implements OnDestroy {
{ path: router.url.replace('/', '') },
] as any);
const layout = (this.route.snapshot.data || {}).layout || findLayout(segments, routes);
const layouts = {
application: this.getComponent('Theme.ApplicationLayoutComponent'),
account: this.getComponent('Theme.AccountLayoutComponent'),
empty: this.getComponent('Theme.EmptyLayoutComponent'),
};
this.layout = layouts
.filter(l => !!l)
.find((l: any) => snq(() => l.type.toLowerCase().indexOf(layout), -1) > -1);
const expectedLayout =
(this.route.snapshot.data || {}).layout || findLayout(segments, routes);
this.layout = layouts[expectedLayout].component;
}
});
}
private getComponent(key: string): ReplaceableComponents.ReplaceableComponent {
return this.store.selectSnapshot(ReplaceableComponentsState.getComponent(key));
}
ngOnDestroy() {}
}

@ -6,7 +6,11 @@ import { Subject } from 'rxjs';
export namespace ABP {
export interface Root {
environment: Partial<Config.Environment>;
requirements: Config.Requirements;
/**
*
* @deprecated To be deleted in v3.0
*/
requirements?: Config.Requirements;
}
export type PagedResponse<T> = {

@ -1,36 +1,36 @@
import { Component, NgModule } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { createRoutingFactory, SpectatorRouting, SpyObject } from '@ngneat/spectator/jest';
import { Store } from '@ngxs/store';
import { createRoutingFactory, SpectatorRouting } from '@ngneat/spectator/jest';
import { NgxsModule, Store } from '@ngxs/store';
import { DynamicLayoutComponent, RouterOutletComponent } from '../components';
import { eLayoutType } from '../enums';
import { ABP } from '../models';
import { DynamicLayoutComponent, RouterOutletComponent } from '../components';
import { ConfigState, ReplaceableComponentsState } from '../states';
import { ApplicationConfigurationService } from '../services';
@Component({
selector: 'abp-layout-application',
template: '<router-outlet></router-outlet>',
})
class DummyApplicationLayoutComponent {
static type = eLayoutType.application;
}
class DummyApplicationLayoutComponent {}
@Component({
selector: 'abp-layout-account',
template: '<router-outlet></router-outlet>',
})
class DummyAccountLayoutComponent {
static type = eLayoutType.account;
}
class DummyAccountLayoutComponent {}
@Component({
selector: 'abp-layout-empty',
template: '<router-outlet></router-outlet>',
})
class DummyEmptyLayoutComponent {
static type = eLayoutType.empty;
}
class DummyEmptyLayoutComponent {}
const LAYOUTS = [DummyApplicationLayoutComponent, DummyAccountLayoutComponent, DummyEmptyLayoutComponent];
const LAYOUTS = [
DummyApplicationLayoutComponent,
DummyAccountLayoutComponent,
DummyEmptyLayoutComponent,
];
@NgModule({
imports: [RouterModule],
@ -47,13 +47,57 @@ class DummyComponent {
constructor(public route: ActivatedRoute) {}
}
const storeData = {
ConfigState: {
routes: [
{
path: '',
wrapper: true,
children: [
{
path: 'parentWithLayout',
layout: eLayoutType.application,
children: [
{ path: 'childWithoutLayout' },
{ path: 'childWithLayout', layout: eLayoutType.account },
],
},
],
},
{ path: 'withData', layout: eLayoutType.application },
,
] as ABP.FullRoute[],
environment: { application: {} },
},
ReplaceableComponentsState: {
replaceableComponents: [
{
key: 'Theme.ApplicationLayoutComponent',
component: DummyApplicationLayoutComponent,
},
{
key: 'Theme.AccountLayoutComponent',
component: DummyAccountLayoutComponent,
},
{
key: 'Theme.EmptyLayoutComponent',
component: DummyEmptyLayoutComponent,
},
],
},
};
describe('DynamicLayoutComponent', () => {
const createComponent = createRoutingFactory({
component: RouterOutletComponent,
stubsEnabled: false,
mocks: [Store],
declarations: [DummyComponent, DynamicLayoutComponent],
imports: [RouterModule, DummyLayoutModule],
mocks: [ApplicationConfigurationService],
imports: [
RouterModule,
DummyLayoutModule,
NgxsModule.forRoot([ConfigState, ReplaceableComponentsState]),
],
routes: [
{ path: '', component: RouterOutletComponent },
{
@ -100,33 +144,13 @@ describe('DynamicLayoutComponent', () => {
});
let spectator: SpectatorRouting<RouterOutletComponent>;
let store: SpyObject<Store>;
const mockStoreData = {
requirements: { layouts: LAYOUTS },
routes: [
{
path: '',
wrapper: true,
children: [
{
path: 'parentWithLayout',
layout: eLayoutType.application,
children: [{ path: 'childWithoutLayout' }, { path: 'childWithLayout', layout: eLayoutType.account }],
},
],
},
{ path: 'withData', layout: eLayoutType.application },
,
] as ABP.FullRoute[],
environment: { application: {} },
};
let storeSpy: jest.SpyInstance;
let store: Store;
beforeEach(async () => {
spectator = createComponent();
store = spectator.get(Store);
storeSpy = jest.spyOn(store, 'selectSnapshot');
storeSpy.mockReturnValue(mockStoreData);
store.reset(storeData);
});
it('should handle application layout from parent abp route and display it', async () => {
@ -159,7 +183,7 @@ describe('DynamicLayoutComponent', () => {
});
it('should not display any layout when layouts are empty', async () => {
storeSpy.mockReturnValue({ ...mockStoreData, requirements: { layouts: [] } });
store.reset({ ...storeData, ReplaceableComponentsState: {} });
spectator.detectChanges();

@ -1,12 +1,29 @@
import { LazyLoadService, AddReplaceableComponent } from '@abp/ng.core';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { LazyLoadService } from '@abp/ng.core';
import { Store } from '@ngxs/store';
import styles from '../constants/styles';
import { ApplicationLayoutComponent } from '../components/application-layout/application-layout.component';
import { AccountLayoutComponent } from '../components/account-layout/account-layout.component';
import { EmptyLayoutComponent } from '../components/empty-layout/empty-layout.component';
@Injectable({ providedIn: 'root' })
export class InitialService {
constructor(private lazyLoadService: LazyLoadService) {
constructor(private lazyLoadService: LazyLoadService, private store: Store) {
this.appendStyle().subscribe();
this.store.dispatch([
new AddReplaceableComponent({
key: 'Theme.ApplicationLayoutComponent',
component: ApplicationLayoutComponent,
}),
new AddReplaceableComponent({
key: 'Theme.AccountLayoutComponent',
component: AccountLayoutComponent,
}),
new AddReplaceableComponent({
key: 'Theme.EmptyLayoutComponent',
component: EmptyLayoutComponent,
}),
]);
}
appendStyle() {

@ -3,7 +3,6 @@ import { CoreModule } from '@abp/ng.core';
import { IdentityConfigModule } from '@abp/ng.identity.config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management.config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
import { LAYOUTS } from '@abp/ng.theme.basic';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@ -20,10 +19,7 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })];
@NgModule({
imports: [
CoreModule.forRoot({
environment,
requirements: {
layouts: LAYOUTS
}
environment
}),
ThemeSharedModule.forRoot(),
AccountConfigModule.forRoot({ redirectUrl: '/' }),

@ -0,0 +1,5 @@
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
}

@ -3,7 +3,6 @@ import { CoreModule } from '@abp/ng.core';
import { IdentityConfigModule } from '@abp/ng.identity.config';
import { SettingManagementConfigModule } from '@abp/ng.setting-management.config';
import { TenantManagementConfigModule } from '@abp/ng.tenant-management.config';
import { LAYOUTS } from '@abp/ng.theme.basic';
import { ThemeSharedModule } from '@abp/ng.theme.shared';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@ -11,11 +10,11 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin';
import { NgxsModule } from '@ngxs/store';
import { OAuthModule } from 'angular-oauth2-oidc';
import { MyProjectNameConfigModule } from '../../projects/my-project-name-config/src/public-api';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';
import { MyProjectNameConfigModule } from '../../projects/my-project-name-config/src/public-api';
const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })];
@ -25,9 +24,6 @@ const LOGGERS = [NgxsLoggerPluginModule.forRoot({ disabled: false })];
ThemeSharedModule.forRoot(),
CoreModule.forRoot({
environment,
requirements: {
layouts: LAYOUTS,
},
}),
OAuthModule.forRoot(),
NgxsModule.forRoot([]),

Loading…
Cancel
Save