Merge branch 'rel-3.2' into dev

pull/5694/head
Halil İbrahim Kalkan 5 years ago
commit 2bc6e2ca84

@ -7,7 +7,9 @@
"formatter",
"md",
"monorepo",
"ngsw",
"npx",
"pwa",
"rootNamespace"
]
}

@ -2,7 +2,7 @@
ABP's localization system is seamlessly integrated to the `Microsoft.Extensions.Localization` package and compatible with the [Microsoft's localization documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization). It adds some useful features and enhancements to make it easier to use in real life application scenarios.
## Volo.Abp.Localization Package
## Installation
> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually.
@ -242,4 +242,5 @@ Both of the samples above produce the output `Hello John, welcome!`.
## See Also
* [Localization in Angular UI](UI/Angular/Localization.md)
* [Localization in Angular UI](UI/Angular/Localization.md)
* [Forms & Validation](UI/AspNetCore/Forms-Validation.md) for the ASP.NET Core MVC / Razor Pages UI

@ -443,7 +443,7 @@ function configureRoutes(routes: RoutesService) {
* `order` is the order of the menu item.
* `layout` is the layout of the BooksModule's routes (there are three types of pre-defined layouts: `eLayoutType.application`, `eLayoutType.account` or `eLayoutType.empty`).
For more information, see the [RoutesService document](https://docs.abp.io/en/abp/latest/UI/Angular/Modifying-the-Menu.md#via-routesservice).
For more information, see the [RoutesService document](../UI/Angular/Modifying-the-Menu.md#via-routesservice).
### Service Proxy Generation

@ -0,0 +1,346 @@
# PWA Configuration
[Progressive Web Apps](https://web.dev/progressive-web-apps/) are web applications which, although not as integrated to the OS as a native app, can take advantage of native features. They can be discovered via search engines, installed on devices with a single tap or click, and shared via a regular link. They also can work offline and get updates when new content is available.
Converting your Angular application to a PWA is easy.
## 1. Install Angular PWA
Run the following command in the root folder of your Angular application:
```shell
yarn ng add @angular/pwa
```
...or...
```shell
npm run ng add @angular/pwa
```
This will install the `@angular/service-worker` package and make your default app a PWA. Alternatively, you may add `project` parameter to target a specific app in your workspace:
```shell
yarn ng add @angular/pwa --project MyProjectName
```
Here is the output of the command:
<img alt="Angular PWA updates and creates files" src="./images/pwa-configuration-ng-add.png" width="400px" style="max-width:100%">
So, Angular CLI updates some files and add a few others:
- **ngsw-config.json** is where the [service worker configuration](https://angular.io/guide/service-worker-config) is placed. Not all PWAs have this file. It is specific to Angular.
- **manifest.webmanifest** is a [web app manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) and provides information about your app in JSON format.
- **icons** are placeholder icons that are referred to in your web app manifest. We will replace these in a minute.
- **angular.json** has following modifications:
- `assets` include _manifest.webmanifest_.
- `serviceWorker` is `true` in production build.
- `ngswConfigPath` refers to _ngsw-config.json_.
- **package.json** has _@angular/service-worker_ as a new dependency.
- **app.module.ts** imports `ServiceWorkerModule` and registers a service worker filename.
- **index.html** has following modifications:
- A `<link>` element that refers to _manifest.webmanifest_.
- A `<meta>` tag that sets a theme color.
## 2. Update the Web App Manifest
### 2.1. Set the Name of Your App
The `name` and the `short_name` properties in the generated manifest are derived from your project name. Let's change them.
Open the _manifest.webmanifest_ file and update `name` and `short_name` props:
```json
{
/* rest of the manifest meta data */
"short_name": "My Project",
"name": "My Project: My Catch-Phrase"
}
```
The short name must be really short because it will be displayed on anywhere with limited space, like the launcher and the home screen.
### 2.2. Add a Description
The `@angular/pwa` schematic we just added does not insert a description to your manifest file, but, according to [web app manifest standards](https://www.w3.org/TR/appmanifest/#description-member), you should.
So, open the _manifest.webmanifest_ file and place the description as seen below:
```json
{
/* rest of the manifest meta data */
"description": "My short project description giving a slightly better idea about my app"
}
```
As a bonus, providing a description [along with other criteria](https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-edgehtml/microsoft-store#criteria-for-automatic-submission) helps Bing web crawler to index your app and automatically submit your app to Microsoft Store in `.appx` format.
### 2.3. Set App Colors
Angular generates the manifest file with a default `theme_color` and `background_color`. Change these according to your brand identity:
Open the _manifest.webmanifest_ file and update `theme_color` and `background_color` properties:
```json
{
/* rest of the manifest meta data */
"theme_color": "#000000",
"background_color": "#ffffff"
}
```
Then open _index.html_ and change the theme color meta tag as below:
```html
<meta name="theme-color" content="#000000" />
```
### 2.4. Replace App Icons & Add Splash Screens
We need to update the icons and add some splash screens. This normally is time-consuming, but we will use the marvelous [pwa-asset-generator](https://github.com/onderceylan/pwa-asset-generator#readme) library.
First, open the _manifest.webmanifest_ file and remove all elements in the `icons` property:
```json
{
/* rest of the manifest meta data */
"icons": []
}
```
Then, run the following command in your terminal (changing the path of course):
```shell
npx pwa-asset-generator /path/to/your/logo.png ./src/assets/pwa -i ./src/index.html -m ./src/manifest.webmanifest
```
Open the _manifest.webmanifest_ file again. You will see this:
```json
{
/* rest of the manifest meta data */
"icons": [
{
"src": "../manifest-icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "../manifest-icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
]
}
```
In addition to updated icons, the library will generate splash screens. However, Apple requires all splash screens to be added in your _index.html_ and displays a blank screen at startup otherwise. So, the following tags will be inserted into the _index.html_ file:
```html
<link
rel="apple-touch-icon"
sizes="180x180"
href="assets/pwa/apple-icon-180.jpg"
/>
<link
rel="apple-touch-icon"
sizes="167x167"
href="assets/pwa/apple-icon-167.jpg"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="assets/pwa/apple-icon-152.jpg"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="assets/pwa/apple-icon-120.jpg"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2048-2732.jpg"
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2732-2048.jpg"
media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1668-2388.jpg"
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2388-1668.jpg"
media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1536-2048.jpg"
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2048-1536.jpg"
media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1668-2224.jpg"
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2224-1668.jpg"
media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1620-2160.jpg"
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2160-1620.jpg"
media="(device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1242-2688.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2688-1242.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1125-2436.jpg"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-2436-1125.jpg"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-828-1792.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1792-828.jpg"
media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1080-1920.jpg"
media="(device-width: 360px) and (device-height: 640px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1920-1080.jpg"
media="(device-width: 360px) and (device-height: 640px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-750-1334.jpg"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1334-750.jpg"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-640-1136.jpg"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="assets/pwa/apple-splash-1136-640.jpg"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)"
/>
```
## 3. Configure Service Worker
### 3.1 Modify Asset Groups
Angular has defined some static files to be cached by the service worker, but they are not 100% accurate. Let's change it.
Open _ngsw-config.json_ file and replace its content with this:
```json
{
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/manifest.webmanifest",
"/*.css",
"/common-es2015.*.js",
"/main-es2015.*.js",
"/polyfills-es2015.*.js",
"/runtime-es2015.*.js",
"/vendor-es2015.*.js"
]
}
},
{
"name": "modules",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/*-es2015.*.js",
"!/common-es2015.*.js",
"!/main-es2015.*.js",
"!/polyfills-es2015.*.js",
"!/runtime-es2015.*.js",
"!/vendor-es2015.*.js"
]
}
},
{
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}
```
In case you want to cache other static files, please refer to the [service worker configuration document](https://angular.io/guide/service-worker-config#assetgroups) on Angular.io.
### 3.2 Set Data Groups
This part is unique to your project. We recommend being very careful about which endpoints to cache. Please refer to [service worker configuration document](https://angular.io/guide/service-worker-config#datagroups) on Angular.io for details.
## What's Next?
- [Config State](./Config-State.md)

@ -140,4 +140,4 @@ export class BookComponent implements OnInit {
## What's Next?
- [Config State](./Config-State.md)
- [PWA Configuration](./PWA-Configuration.md)

@ -67,7 +67,7 @@ With the options above, the toast overlay looks like this:
![toast](./images/toast.png)
### How to Remove a Toast Overlay
### How to Remove a Toast Overlay
The open toast overlay can be removed manually via the `remove` method by passing the `id` of toast:

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

@ -0,0 +1,206 @@
# ASP.NET Core MVC / Razor Pages: Forms & Validation
ABP Framework provides infrastructure and conventions to make easier to create forms, localize display names for the form elements and handle server & client side validation;
* [abp-dynamic-form](Tag-Helpers/Dynamic-Forms.md) tag helper automates **creating a complete form** from a C# model class: Creates the input elements, handles localization and client side validation.
* [ABP Form tag helpers](Tag-Helpers/Form-elements.md) (`abp-input`, `abp-select`, `abp-radio`...) render **a single form element** with handling localization and client side validation.
* ABP Framework automatically **localizes the display name** of a form element without needing to add a `[DisplayName]` attribute.
* **Validation errors** are automatically localized based on the user culture.
> This document is for the **client side validation** and it doesn't cover the server side validation. Check the [validation document](../../Validation.md) for server side validation infrastructure.
## The Classic Way
In a typical Bootstrap based ASP.NET Core MVC / Razor Pages UI, you [need to write](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation#client-side-validation) such a boilerplate code to create a simple form element:
````html
<div class="form-group">
<label asp-for="Movie.ReleaseDate" class="control-label"></label>
<input asp-for="Movie.ReleaseDate" class="form-control" />
<span asp-validation-for="Movie.ReleaseDate" class="text-danger"></span>
</div>
````
You can continue to use this approach if you need or prefer it. However, ABP Form tag helpers can produce the same output with a minimal code.
## ABP Dynamic Forms
[abp-dynamic-form](Tag-Helpers/Dynamic-Forms.md) tag helper completely automates the form creation. Take this model class as an example:
```csharp
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace MyProject.Web.Pages
{
public class MovieViewModel
{
[Required]
[StringLength(256)]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
[TextArea]
[StringLength(1000)]
public string Description { get; set; }
public Genre Genre { get; set; }
public float? Price { get; set; }
public bool PreOrder { get; set; }
}
}
```
It uses the data annotation attributes to define validation rules and UI styles for the properties. `Genre`, is an `enum` in this example:
````csharp
namespace MyProject.Web.Pages
{
public enum Genre
{
Classic,
Action,
Fiction,
Fantasy,
Animation
}
}
````
In order to create the form in a razor page, create a property in your `PageModel` class:
```csharp
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MyProject.Web.Pages
{
public class CreateMovieModel : PageModel
{
[BindProperty]
public MovieViewModel Movie { get; set; }
public void OnGet()
{
Movie = new MovieViewModel();
}
public async Task OnPostAsync()
{
if (ModelState.IsValid)
{
//TODO: Save the Movie
}
}
}
}
```
Then you can render the form in the `.cshtml` file:
```html
@page
@model MyProject.Web.Pages.CreateMovieModel
<h2>Create a new Movie</h2>
<abp-dynamic-form abp-model="Movie" submit-button="true" />
```
The result is shown below:
![abp-dynamic-form-result](../../images/abp-dynamic-form-result.png)
See the *Localization & Validation* section below to localize the field display names and see how the validation works.
> See [its own document](Tag-Helpers/Dynamic-Forms.md) for all options of the `abp-dynamic-form` tag helper.
## ABP Form Tag Helpers
`abp-dynamic-form` covers most of the scenarios and allows you to control and customize the form using the attributes.
However, if you want to **render the form body yourself** (for example, you may want to fully control the **form layout**), you can directly use the [ABP Form Tag Helpers](Tag-Helpers/Form-elements.md). The same auto-generated form above can be created using the ABP Form Tag Helpers as shown below:
```html
@page
@model MyProject.Web.Pages.CreateMovieModel
<h2>Create a new Movie</h2>
<form method="post">
<abp-input asp-for="Movie.Name"/>
<abp-input asp-for="Movie.ReleaseDate"/>
<abp-input asp-for="Movie.Description"/>
<abp-select asp-for="Movie.Genre"/>
<abp-input asp-for="Movie.Price"/>
<abp-input asp-for="Movie.PreOrder"/>
<abp-button button-type="Primary" type="submit">Save</abp-button>
</form>
```
> See the [ABP Form Tag Helpers](Tag-Helpers/Form-elements.md) document for details of these tag helpers and their options.
## Validation & Localization
Both of the Dynamic Form and the Form Tag Helpers **automatically validate** the input based on the data annotation attributes and shows validation error messages on the user interface. Error messages are **automatically localized** based on the current culture.
**Example: User leaves empty a requires string property**
![abp-form-input-validation-error](../../images/abp-form-input-validation-error.png)
The error message below is shown if the language is French:
![abp-form-input-validation-error](../../images/abp-form-input-validation-error-french.png)
Validation errors are already [translated](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization) a lot of languages. You can [contribute](../../Contribution/Index.md) to the translation for your own language or override the texts for your own application by following the [localization](../../Localization.md) documentation.
## Display Name Localization
ABP Framework uses the property name as the field name on the user interface. You typically want to [localize](../../Localization.md) this name based on the current culture.
ABP Framework can conventionally localize the fields on the UI when you add the localization keys to the localization JSON files.
Example: French localization for the *Name* property (add into the `fr.json` in the application):
````js
"Name": "Nom"
````
Then the UI will use the given name for French language:
![abp-form-input-validation-error](../../images/abp-form-input-validation-error-french-name.png)
### Using the `DisplayName:` Prefix
Directly using the property name as the localization key may be a problem if you need to use the property name for other purpose, which a different translation value. In this case, use the `DisplayName:` prefix for the localization key:
````js
"DisplayName:Name": "Nom"
````
ABP prefers to use the `DisplayName:Name` key over the `Name` key if it does exists.
### Using a Custom Localization Key
If you need, you can use the `[DisplayName]` attribute to specify the localization key for a specific property:
````csharp
[DisplayName("MyNameKey")]
public string Name { get; set; }
````
In this case, you can add an entry to the localization file using the key `MyNameKey`.
> If you use the `[DisplayName]` but not add a corresponding entity to the localization file, then ABP Framework shows the given key as the field name, `MyNameKey` for this case. So, it provides a way to specify a hard coded display name even if you don't need to use the localization system.
## See Also
* [Server Side Validation](../../Validation.md)

@ -1,7 +1,5 @@
# Dynamic Forms
`Warning:` Before getting into this document, be sure that you have clearly understood [abp form elements](Form-elements.md) document.
## Introduction
`abp-dynamic-form` creates a bootstrap form for a given c# model.
@ -272,4 +270,8 @@ You can set it yourself by using `[Display()]` attribute of Asp.Net Core. You ca
````csharp
[Display(Name = "Name")]
public string Name { get; set; }
````
````
## See Also
* [Form Elements](Form-elements.md)

@ -392,7 +392,7 @@
"text": "User Interface",
"items": [
{
"text": "ASP.NET Core",
"text": "ASP.NET Core MVC / Razor Pages",
"items": [
{
"text": "Client Side Package Management",
@ -402,6 +402,10 @@
"text": "Bundling & Minification",
"path": "UI/AspNetCore/Bundling-Minification.md"
},
{
"text": "Forms & Validation",
"path": "UI/AspNetCore/Forms-Validation.md"
},
{
"text": "Tag Helpers",
"path": "UI/AspNetCore/Tag-Helpers/Index.md",
@ -473,6 +477,10 @@
{
"text": "Service Proxies",
"path": "UI/Angular/Service-Proxies.md"
},
{
"text": "PWA Configuration",
"path": "UI/Angular/PWA-Configuration.md"
}
]
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Loading…
Cancel
Save