### Run development server Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/README.md Starts a local development server that automatically reloads on file changes. ```bash ng serve ``` -------------------------------- ### Get CLI help Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/README.md Displays help information for Angular CLI commands. ```bash ng help ``` -------------------------------- ### Complete Navigation Property Pattern Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md A full example demonstrating multiple foreign key relationships using convention-based resolution. ```csharp public class PostGetFullDto : ChangeTrackingDtoBase { // Required in the form (non-nullable long) [Display(Name = "Author")] public long AuthorId { get; set; } // Navigation property resolved by convention: "AuthorId" → "Author" // Must be nullable; excluded from serialization [JsonIgnore] public virtual AuthorDto? Author { get; set; } // Optional in the form (nullable long?) [Display(Name = "Category")] public long? CategoryId { get; set; } // Navigation property resolved by convention: "CategoryId" → "Category" [JsonIgnore] public virtual CategoryDto? Category { get; set; } } ``` -------------------------------- ### Define TCreateDto for record creation Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Example of a DTO used for creating records, which does not inherit from base classes. ```csharp public class PostCreateDto { [Display(Name = "Author")] public long AuthorId { get; set; } [Display(Name = "Blog")] public long BlogId { get; set; } [Required] public string Headline { get; set; } = default!; public PostState State { get; set; } [DataType(DataType.MultilineText)] [Required] public string Text { get; set; } = default!; // Navigation property — used for HAL-Forms options generation (see below) [JsonIgnore] public AuthorDto? Author { get; set; } [JsonIgnore] public BlogGetListDto? Blog { get; set; } } ``` -------------------------------- ### Define TGetListDto for list responses Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Example of a DTO inheriting from ChangeTrackingDtoBase for compact list views. ```csharp public class PostListDto : ChangeTrackingDtoBase { public required string Headline { get; set; } [Display(Name = "Author")] public long AuthorId { get; set; } [Display(Name = "Blog")] public long BlogId { get; set; } // Navigation properties — used for HAL-Forms options generation (see below) [JsonIgnore] public virtual AuthorDto? Author { get; set; } [JsonIgnore] public virtual BlogGetFullDto? Blog { get; set; } } ``` -------------------------------- ### Define TUpdateDto for record updates Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Example of a DTO inheriting from ConcurrentDtoBase to support optimistic concurrency. ```csharp public class PostUpdateDto : ConcurrentDtoBase { [Display(Name = "Author")] public long AuthorId { get; set; } [Display(Name = "Blog")] public long BlogId { get; set; } [Required] public string Headline { get; set; } = default!; public PostState State { get; set; } [Required] [DataType(DataType.MultilineText)] public string Text { get; set; } = default!; } ``` -------------------------------- ### Build project Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/README.md Compiles the application into the dist directory. ```bash ng build ``` -------------------------------- ### Configure Aspire Distributed Application Source: https://github.com/wertzui/restworld/blob/main/doc/health-and-operations.md Sets up a distributed application with a database connection and project references for an API and Angular frontend. ```csharp using Projects; var builder = DistributedApplication.CreateBuilder(args); var database = builder.AddConnectionString("MyDatabase"); // Optional SQL Server container // var database = builder.AddSqlServer("SQL-Server") // .AddDatabase("MyDatabase"); var apiService = builder.AddProject("MyApi") .WithReference(database); // Angular frontend hosted via ASP.NET Core // Note: OTLP for Angular currently requires gRPC support (see https://github.com/dotnet/aspire/pull/4197) var frontendService = builder.AddProject("MyApi-Client-Angular") .WithReference(apiService); var app = builder.Build(); await app.RunAsync(); ``` -------------------------------- ### Define Application Routes and Menus Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-setup.md Configures application routing and menu structures, mixing RESTworld-provided views with custom components. ```ts export const AppRoutes: Route[] = [ { path: '', component: HomeComponent, pathMatch: 'full' }, { path: 'edit/postsForBlog/:apiName/:rel', component: PostsForBlogComponent }, { path: 'list/postWithAuthor', runGuardsAndResolvers: 'always', component: PostWithAuthorListComponent }, { path: 'edit/postWithAuthor/:apiName/:rel/:uri', component: PostWithAuthorComponent }, { path: 'list/:apiName/:rel', runGuardsAndResolvers: 'always', component: RESTworldListViewComponent }, { path: 'edit/:apiName/:rel/:uri', component: RESTworldEditViewComponent } ]; export const AppMenu: MenuItem[] = [ { label: 'Examples with defaults', items: [ { label: 'Blogs', routerLink: ['list', 'ExampleBlog', 'MyEx:Blog'], queryParams: { $orderby: 'id desc' } }, { label: 'Authors', routerLink: ['list', 'ExampleBlog', 'MyEx:Author'] } ] } // ...additional menu items ]; ``` -------------------------------- ### Define App Routes and Menu Source: https://github.com/wertzui/restworld/blob/main/src/RESTworld/RESTworld.Client.Angular/projects/ngx-restworld-client/README.md Configure application routes using DeepLinkingWrapperComponent and define the navigation menu structure. ```typescript export const AppRoutes: DeepLinkingRoute[] = [ { path: '', component: HomeComponent, pathMatch: 'full' }, { path: 'list/:apiName/:rel', runGuardsAndResolvers: 'always', component: DeepLinkingWrapperComponent, wrappedComponent: RESTworldListViewComponent, deepLinking: { params: [ { name: 'apiName', type: 'string' }, { name: 'rel', type: 'string' } ], queryParams: [ { name: 'editLink', type: 'string' } ] } }, { path: 'edit/:apiName/:uri', component: DeepLinkingWrapperComponent, wrappedComponent: RESTworldEditViewComponent, deepLinking: { params: [ { name: 'apiName', type: 'string' }, { name: 'uri', type: 'string' } ] } }, { path: 'postWithAuthor/:apiName/:uri', component: DeepLinkingWrapperComponent, wrappedComponent: PostWithAuthorComponent, deepLinking: { params: [ { name: 'apiName', type: 'string' }, { name: 'uri', type: 'string' } ] } } ]; export const AppMenu: MenuItem[] = [ { label: "Examples with defaults", items: [ { label: "Blogs", routerLink: ['list', 'ExampleBlog', 'MyEx:Blog'] }, { label: "Authors", routerLink: ['list', 'ExampleBlog', 'MyEx:Author'] }, { label: "Posts", routerLink: ['list', 'ExampleBlog', 'MyEx:Post'] }] }, { label: "Examples with custom templates", items: [ { label: "Posts with author", routerLink: ['list', 'ExampleBlog', 'MyEx:Post'], queryParams: { editLink: '/postWithAuthor'} } ] }]; ``` -------------------------------- ### Fetching list endpoints Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-dotnet.md Uses GetAllPagesListAsync to retrieve resources while automatically following pagination links. ```csharp using HAL.Common; using RESTworld.Common; using RESTworld.Common.Dtos; var response = await exampleBlog.GetAllPagesListAsync("Post", curie: "MyEx"); if (!response.Succeeded || response.Resource?.Embedded is null) { // handle errors (response.StatusCode, response.Details...) return; } if (response.Resource.Embedded.TryGetValue(Constants.ListItems, out var posts) && posts is not null) { foreach (var post in posts) { var dto = post.State as PostGetFullDto; Console.WriteLine($"{dto?.Title} ({dto?.Id})"); } } ``` -------------------------------- ### Configure RESTworld Client settings Source: https://github.com/wertzui/restworld/blob/main/doc/configuration.md Specify target API instances for client libraries to enable automatic version negotiation via the Accept header. ```json "RESTworld": { "ClientSettings": { "ApiUrls": [ { "Name": "MyApi", "Url": "https://myapi.example.com/", "Version": 42 } ] } } ``` -------------------------------- ### Configure Vitest Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-setup.md Sets up the Vitest test runner environment and reporting options in vitest.config.ts. ```ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { globals: true, environment: 'jsdom', includeTaskLocation: true, reporters: ["default", "junit"], outputFile: "test-results.xml", coverage: { provider: "istanbul", reporter: ["cobertura"], } } }) ``` -------------------------------- ### Registering RESTworld clients Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-dotnet.md Configures RESTworld clients using AddRestWorldClients on the HostApplicationBuilder, allowing for optional HttpClient decoration. ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using RESTworld.Client.Net; var builder = Host.CreateApplicationBuilder(args); builder.AddRestWorldClients(new Dictionary?> { ["ExampleBlog"] = (services, http) => { // Optional: decorate the HttpClient, e.g., add auth headers or resilience policies. // var tokenProvider = services.GetRequiredService(); // http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenProvider.GetToken()); } }); var host = builder.Build(); await host.StartAsync(); ``` -------------------------------- ### Run unit tests Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/README.md Executes unit tests using the Karma test runner. ```bash ng test ``` -------------------------------- ### Define a TGetFullDto class Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Used for fetching single records, this DTO extends ChangeTrackingDtoBase and includes fields for detail views. ```csharp [HasHistory] // optional — signals that this entity has a history endpoint public class PostGetFullDto : ChangeTrackingDtoBase { [Display(Name = "Author")] public long AuthorId { get; set; } [Display(Name = "Blog")] public long BlogId { get; set; } [Required] public string Headline { get; set; } = default!; public PostState State { get; set; } [Required] [DataType(DataType.MultilineText)] public string Text { get; set; } = default!; // Navigation properties — used for HAL-Forms options generation (see below) [JsonIgnore] public virtual AuthorDto? Author { get; set; } [JsonIgnore] public virtual BlogGetFullDto? Blog { get; set; } } ``` -------------------------------- ### Define a TQueryDto class Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Acts as an intermediate representation for OData queries, exposing properties and navigation properties for filtering and sorting. ```csharp public class PostQueryDto : ChangeTrackingDtoBase { // Scalar properties — can be filtered and sorted by OData public long AuthorId { get; set; } public long BlogId { get; set; } public string Headline { get; set; } = default!; public PostState State { get; set; } public string Text { get; set; } = default!; // Navigation properties — included so OData can filter on Author.LastName, Blog.Name etc. public virtual AuthorDto? Author { get; set; } public virtual BlogGetFullDto? Blog { get; set; } } ``` -------------------------------- ### Bootstrap RESTworld API Source: https://github.com/wertzui/restworld/blob/main/doc/getting-started.md Configures the RESTworld services, database context, and CRUD pipelines in the ASP.NET Core Program.cs file. ```csharp using Microsoft.AspNetCore.Builder; var builder = WebApplication.CreateBuilder(args); builder.AddRestWorld(); var services = builder.Services; // Database builder.AddDbContextFactoryWithDefaults(); // Optional database migration during startup builder.MigrateDatabaseDuringStartup(); // Default CRUD pipeline with a Mapperly mapper builder.AddCrudPipeline(); builder.AddCrudMapper(); // Versioned pipelines (optional) — each version gets its own mapper builder.AddCrudPipeline(new ApiVersion(1, 0), true); builder.AddCrudMapper(); builder.AddCrudPipeline(new ApiVersion(2, 0)); builder.AddCrudMapper(); // Pipelines with custom services (optional) builder.AddCrudPipelineWithCustomService(); builder.AddCrudMapper(); // Read-only pipeline with custom service builder.AddReadPipelineWithCustomService(); builder.AddReadMapper(); // Extra validators (optional) builder.AddValidator(); var app = builder.Build(); app.UseRestWorld(); await app.RunAsync(); ``` -------------------------------- ### Configure RESTworld API settings Source: https://github.com/wertzui/restworld/blob/main/doc/configuration.md Define global API behavior including pagination limits, link relation prefixes, and versioning strategies. ```json "RESTworld": { "MaxNumberForListEndpoint": 10, "Curie": "MyEx", "CalculateTotalCountForListEndpoint": true, "DisableAuthorization": false, "Versioning": { "AllowQueryParameterVersioning": false, "DefaultVersion": "1.0", "ParameterName": "v" } } ``` -------------------------------- ### RESTworld Simple Display Template Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/components/restworld-displays/restworld-display-simple/restworld-display-simple.component.html Uses a switch block to determine the display format for different property types, excluding specific audit fields from rendering. ```html @let prop = property(); @let val = computedValue(); @switch (prop.type) { @case (PropertyType.Text) { @if (prop.name === 'createdBy' || prop.name === 'lastChangedBy') { } @else { {{val | propertyTypeFormat:prop.type}} } } @case (PropertyType.Bool) { } @default { {{val | propertyTypeFormat:prop.type}} } } ``` -------------------------------- ### Submitting form endpoints Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-dotnet.md Retrieves a form template and submits a payload to create a new resource. ```csharp var href = client.GetLinkFromHome("Blog", "Post", "MyEx"); var uri = new Uri(href); var createForm = await client.GetFormAsync(uri); var template = createForm.Templates["_default"]; var target = template.target; var newBlog = createForm.State; newBlog.Title = "A very good title"; var response = await exampleBlog.SendFormAsync( template.Method, new Uri(template.Target), newBlog ); if (response.Succeeded) { var createdBlog = result.Resource.State; Console.WriteLine($"Created blog with id {createdBlog.Id}"); } ``` -------------------------------- ### Register Authorization Handlers in C# Source: https://github.com/wertzui/restworld/blob/main/doc/authorization.md Register concrete or generic authorization handlers and configure REST pipelines with authorization using the service collection. ```csharp // Concrete handler for a pipeline services.AddAuthorizationHandler(); // Generic handler shared across pipelines services.AddAuthorizationHandler, TEntity, TCreateDto, TQueryDto, TGetListDto, TGetFullDto, TUpdateDto>(); // Pipeline + handler shortcuts services.AddRestPipelineWithAuthorization(); services.AddRestPipelineWithAuthorization>(); // Pipelines with custom services services.AddRestPipelineWithCustomServiceAndAuthorization(); services.AddRestPipelineWithCustomServiceAndAuthorization>(); ``` -------------------------------- ### Render a HAL-driven data grid with rw-table Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-components.md Use this component to display data from a HAL list response with support for filtering, sorting, and inline editing. ```html ``` -------------------------------- ### Register Foreign Key for Custom Pipelines Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Use this method when building custom services or registering additional types not covered by standard pipeline extensions. ```csharp // Only needed for custom setups builder.AddForeignKeyForFormTo(); ``` -------------------------------- ### Bootstrap RESTworld with Angular SPA Source: https://github.com/wertzui/restworld/blob/main/doc/getting-started.md Configures the RESTworld services and middleware specifically for hosting an Angular SPA within an ASP.NET Core project. ```csharp using Microsoft.AspNetCore.Builder; var builder = WebApplication.CreateBuilder(args); builder.AddRestWorldWithSpaFrontend(); var app = builder.Build(); app.UseRestWorldWithSpaFrontend(); await app.RunAsync(); ``` -------------------------------- ### Compose Application Shell Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-setup.md Defines the root component layout using Angular router outlets and PrimeNG toast components. ```html
``` -------------------------------- ### Registering Foreign Key Factories Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Register a foreign key factory for a specific DTO type within the pipeline. ```csharp services.AddForeignKeyForFormTo(); ``` -------------------------------- ### Bootstrap RESTworld in main.ts Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-setup.md Registers RESTworld providers, Angular Router, and PrimeNG configuration within the application bootstrap process. ```ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { provideAnimations } from '@angular/platform-browser/animations'; import { provideRouter, withRouterConfig, withComponentInputBinding } from '@angular/router'; import { definePreset } from '@primeng/themes'; import { providePrimeNG } from 'primeng/config'; import Aura from '@primeng/themes/aura'; import * as base from '@primeng/themes/aura/base'; import { MessageService, ConfirmationService } from 'primeng/api'; import { provideRestWorld, provideCustomAvatarGenerator } from './app/ngx-restworld-client/provide-restworld'; import { ExampleAvatarGenerator } from './app/ExampleAvatarGenerator'; import { AppRoutes } from './app/app.routes'; bootstrapApplication(AppComponent, { providers: [ provideRestWorld(), provideCustomAvatarGenerator(ExampleAvatarGenerator), provideHttpClient(withInterceptorsFromDi()), provideRouter(AppRoutes, withRouterConfig({ onSameUrlNavigation: 'reload' }), withComponentInputBinding()), provideAnimations(), providePrimeNG({ ripple: true, theme: { preset: definePreset(Aura, { semantic: { primary: base.default.primitive?.blue } }) } }), MessageService, ConfirmationService ] }); ``` -------------------------------- ### Display Selected Items in Dropdown Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/components/restworld-displays/restworld-display-dropdown/restworld-display-dropdown.component.html Renders the selected items from the options manager as a comma-separated list. ```html @for (item of optionsManager.selectedItems(); track item; let last = $last) { {{optionsManager.getLabel()($any(item))}}{{last ? "" : ", "}} } ``` -------------------------------- ### Register AutoMapper-backed Mappers Source: https://github.com/wertzui/restworld/blob/main/doc/mapping-and-versioning.md Configure AutoMapper services and register CRUD or read-only pipelines using built-in extension methods. ```csharp // In Program.cs services.AddAutoMapper(AutoMapperConfiguration.ConfigureAutomapper); // Register an AutoMapper-backed mapper for a CRUD pipeline builder.AddCrudAutoMapper(); // Register an AutoMapper-backed mapper for a read-only pipeline builder.AddReadAutoMapper(); ``` -------------------------------- ### Register Versioned CRUD Pipelines and Mappers Source: https://github.com/wertzui/restworld/blob/main/doc/mapping-and-versioning.md Configure separate pipelines and mappers for different API versions to isolate mapping logic. ```csharp // Versioned pipelines with separate mappers builder.AddCrudPipeline(new ApiVersion(1, 0), true); builder.AddCrudMapper(); builder.AddCrudPipeline(new ApiVersion(2, 0)); builder.AddCrudMapper(); ``` -------------------------------- ### Defining Nullable Navigation Properties Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Navigation properties must be nullable and decorated with JsonIgnore to prevent deserialization errors. ```csharp // ✓ Correct — nullable [JsonIgnore] public virtual AuthorDto? Author { get; set; } // ✗ Wrong — will throw during deserialization [JsonIgnore] public virtual AuthorDto Author { get; set; } = default!; ``` -------------------------------- ### Configure AutoMapper Profiles Source: https://github.com/wertzui/restworld/blob/main/doc/mapping-and-versioning.md Define bidirectional mappings and custom member transformations within an AutoMapperConfiguration class. ```csharp using AutoMapper; using MyApi.Common.Dtos; using MyApi.Common.Enums; using MyApi.Data.Models; namespace MyApi.Business; public static class AutoMapperConfiguration { public static void ConfigureAutomapper(IMapperConfigurationExpression config) { // Simple bidirectional mapping config .CreateMap() .ReverseMap(); // Add mappings for each version as needed config .CreateMap() .ReverseMap(); config .CreateMap() .ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.FirstName + " " + src.LastName)) .ReverseMap() .ForMember(dst => dst.FirstName, opt => opt.MapFrom(src => src.Name.Split(new[] { ' ' }, 2)[0])) .ForMember(dst => dst.LastName, opt => opt.MapFrom(src => src.Name.Split(new[] { ' ' }, 2)[1])); // Add more mappings as your domain evolves } } ``` -------------------------------- ### Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-components.md The component renders a PrimeNG-based data grid driven by HAL search templates. It supports inline editing, filtering, sorting, and row selection. ```APIDOC ## ### Description Renders a data grid based on HAL search templates. It connects to filtering logic and supports row selection and inline editing. ### Inputs - **apiName** (string) - Required - RESTworld API alias. - **searchTemplate** (object) - Required - HAL template for columns, filters, and sorting. - **rows** (array) - Required - Data displayed in the grid. - **editTemplate** (object) - Optional - Enables editable cells. - **oDataParameters** (object) - Optional - Filter and paging options. - **rowMenu** (array) - Optional - PrimeNG MenuItem array for row actions. ### Example ``` -------------------------------- ### Implement ICrudAuthorizationHandler in C# Source: https://github.com/wertzui/restworld/blob/main/doc/authorization.md Create a custom authorization handler by implementing the ICrudAuthorizationHandler interface to define access logic for CRUD operations. ```csharp public class MyAuthorizationHandler : ICrudAuthorizationHandler { // Inject dependencies such as IUserAccessor, services, etc. public Task AuthorizeCreateAsync(...) { /* logic */ } public Task AuthorizeGetListAsync(...) { /* logic */ } public Task AuthorizeGetAsync(...) { /* logic */ } public Task AuthorizeUpdateAsync(...) { /* logic */ } public Task AuthorizeDeleteAsync(...) { /* logic */ } } ``` -------------------------------- ### Configure Angular Proxy Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-setup.md Defines proxy rules in src/proxy.conf.js to forward requests to the ASP.NET Core host during development. ```js const PROXY_CONFIG = [ { context: [ '/settings', ], target: process.env["services__ExampleBlog-Client__https__0"], secure: process.env["NODE_ENV"] !== "development" } ]; console.log(`Proxy configuration:\n${JSON.stringify(PROXY_CONFIG, null, 2)}`) module.exports = PROXY_CONFIG; ``` -------------------------------- ### Run end-to-end tests Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/README.md Executes end-to-end tests using Protractor. ```bash ng e2e ``` -------------------------------- ### RESTworld Table Template Structure Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/components/restworld-table/restworld-table.component.html The template iterates over columns to render headers and input fields, conditionally displaying menu columns based on component state. ```html @for (col of columns; track col.name) { {{col.prompt}} @if(!col.readOnly) { @if (lazy()) { } } } @if (showMenuColumn()) { } @for (col of columns; track col.name) { @let fomrArray = formArray(); @if (showInputField(col) && fomrArray) { @let formControl = getFormGroupAtIndex(rowIndex); @if (formControl) { } } @else { } } @if (showMenuColumn()) { @if (showRowMenuAsColumn()) { } } ``` -------------------------------- ### RESTworld Input Component Template Structure Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/components/restworld-inputs/restworld-input/restworld-input.component.html This template uses conditional logic to determine how to render properties based on their options and types. ```html @if (property()) { @if (property().options) { } @else { @switch (property().type) { @case (PropertyType.Object) { } @case (PropertyType.Collection) { } @default { } } } } ``` -------------------------------- ### Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-components.md The component renders a HAL form with validation and built-in action buttons. It interacts with the RESTworld client service for data submission and concurrency management. ```APIDOC ## ### Description Renders a HAL form with validation messages and default action buttons. It supports custom layouts via content and button slots. ### Inputs - **apiName** (string) - Required - RESTworld API alias. - **rel** (string) - Required - Relation for the current form. - **template** (object) - Required - HAL template for the form model. ### Outputs - **afterSubmit** - Emitted on successful submission. - **afterDelete** - Emitted on successful deletion. - **valueChanges** - Emits when form data changes. ### Example ``` -------------------------------- ### Define CRUD pipeline generic parameters Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md The six generic parameters used to type a full CRUD pipeline. ```text TEntity, TCreateDto, TQueryDto, TGetListDto, TGetFullDto, TUpdateDto ``` -------------------------------- ### Scaffold Angular components Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/README.md Generates new Angular artifacts such as components, directives, or services. ```bash ng generate component component-name ``` ```bash ng generate directive|pipe|service|class|guard|interface|enum|module ``` -------------------------------- ### Customize rw-form layout and buttons Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-components.md Override default form rendering by providing custom templates for the content and buttons slots. ```html ``` -------------------------------- ### Register Custom Mappers Source: https://github.com/wertzui/restworld/blob/main/doc/mapping-and-versioning.md Register custom implementations of mapper interfaces using the appropriate extension methods. ```csharp services.AddCrudMapper(); services.AddReadMapper(); ``` -------------------------------- ### Display Selection and Form Values Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/posts-for-blog/posts-for-blog.component.html Displays the current selection and form array values as JSON strings when an API name is present. ```html {{selection() | json}} ``` ```html {{formArray.value | json}} ``` -------------------------------- ### Multi-value Foreign Key Configuration Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Use explicit ForeignKey attributes for many-to-many relationships involving collections of IDs. ```csharp public class TestDto : ConcurrentDtoBase { [ForeignKey(nameof(Blogs))] // explicit — "BlogIds" does not end in "Id" [DisplayName("Blogs")] public required IReadOnlyCollection BlogIds { get; set; } [JsonIgnore] public virtual ICollection? Blogs { get; } // must be nullable } ``` -------------------------------- ### Register a Mapperly Mapper Source: https://github.com/wertzui/restworld/blob/main/doc/mapping-and-versioning.md Use the AddCrudMapper extension method to register the mapper within the application builder. ```csharp builder.AddCrudMapper(); ``` -------------------------------- ### RESTworld Form Template Structure Source: https://github.com/wertzui/restworld/blob/main/src/RESTworld/RESTworld.Client.Angular/projects/ngx-restworld-client/src/lib/components/restworld-form/restworld-form.component.html The template uses Angular control flow to conditionally render the form, loading indicators, and button slots based on component state. ```html @if (formGroup() !== undefined && template() !== undefined) { @if (isLoading()) { } @if (beforeButtonsRef(); as beforeButtons) { } @if (afterButtonsRef(); as afterButtons) { } } ``` -------------------------------- ### Resolving RESTworld clients Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-dotnet.md Retrieves the registered IRestWorldClientCollection from the service provider to access specific clients. ```csharp using Microsoft.Extensions.DependencyInjection; using RESTworld.Common.Client; var clients = host.Services.GetRequiredService(); var exampleBlog = clients.GetClient("ExampleBlog"); ``` -------------------------------- ### Configure Angular Test Target Source: https://github.com/wertzui/restworld/blob/main/doc/client-development-angular-setup.md Updates the angular.json test architect target to use the Vitest configuration. ```json "test": { "builder": "@angular/build:unit-test", "options": { "buildTarget": "exampleblog.client.angular:build:unit-tests", "runnerConfig": "vitest.config.ts" } } ``` -------------------------------- ### RESTworld Edit View Template Structure Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/views/restworld-edit-view/restworld-edit-view.component.html The template uses Angular control flow syntax to manage loading states, template rendering, and conditional tab display. ```html @if (newHref()) { } @if (templates.isLoading()) { Loading } @else { @for (template of templates.value(); track template; let i = $index) { {{template.title}} } @if (extraTabsRef) { Extra } } @if (templates.isLoading()) { @for(i of [1, 2, 3, 4, 5]; track i) { } } @else { @for (template of templates.value(); track template; let i = $index) { } @if (extraTabsRef) { } } ``` -------------------------------- ### RESTworld Dropdown Template Structure Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/components/restworld-inputs/restworld-input-dropdown/restworld-input-dropdown.component.html The template uses conditional logic to switch between template-driven and reactive form approaches for rendering dropdown items. ```html @if (useTemplateDrivenForms()) { {{optionsManager.getLabel()(selectedItem)}} {{optionsManager.getLabel()(item)}} } @else { {{optionsManager.getLabel()(selectedItem)}} {{optionsManager.getLabel()(item)}} } @if (useTemplateDrivenForms()) { @for (item of items; track item; let i = $index) { } {{optionsManager.getLabel()(item)}} } @else { @for (item of items; track item; let i = $index) { } {{optionsManager.getLabel()(item)}} } @if(!optionsManager.options().maxItems || optionsManager.options().maxItems == 1) { } @else { } ``` -------------------------------- ### RESTworld Display Component HTML Structure Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/components/restworld-displays/restworld-display/restworld-display.component.html This template uses Angular control flow to conditionally render UI elements based on the property type. ```html @if (property().type !== PropertyType.Hidden) { @if (property().options) { } @else { @switch (property().type) { @case (PropertyType.Object) { } @case (PropertyType.Collection) { } @default { } } } } ``` -------------------------------- ### Define a Mapperly Mapper Source: https://github.com/wertzui/restworld/blob/main/doc/mapping-and-versioning.md Derive from CrudMapperlyMapperBase and annotate with [Mapper] to enable compile-time source-generated mapping. ```csharp using RESTworld.Business.Mapping.Mapperly; using Riok.Mapperly.Abstractions; namespace MyApi.Business.Mapping; [Mapper] public partial class BlogMapper : CrudMapperlyMapperBase { // When creating an entry, everything on ChangeTrackingEntityBase should be ignored. [MapperRequiredMapping(RequiredMappingStrategy.Source)] public override partial Blog MapCreateToEntity(BlogCreateDto createDto); // Foreign Keys should be ignored [MapperRequiredMapping(RequiredMappingStrategy.Target)] public override partial BlogGetFullDto MapEntityToFull(Blog entity); public override partial Expression> MapEntityToFullExpression(); // Foreign Keys should be ignored [MapperRequiredMapping(RequiredMappingStrategy.Target)] public override partial BlogGetFullDto MapQueryToFull(BlogQueryDto entity); public override partial IQueryable MapQueryToFullQueryable(IQueryable entities); // Foreign Keys should be ignored [MapperRequiredMapping(RequiredMappingStrategy.Target)] public override partial BlogGetListDto MapQueryToList(BlogQueryDto queryDto); public override partial IQueryable MapQueryToListQueryable(IQueryable queryDtos); public override partial IQueryable MapEntityToQuery(IQueryable entities); // When updating an entry, everything on ChangeTrackingEntityBase except the Id should be ignored. [MapperRequiredMapping(RequiredMappingStrategy.Source)] public override partial void MapUpdateToEntity(BlogUpdateDto updateDto, Blog entity); } ``` -------------------------------- ### Conditional Input Rendering Template Source: https://github.com/wertzui/restworld/blob/main/src/Example/ExampleBlog/Exampleblog.Client.Angular/src/app/ngx-restworld-client/components/restworld-inputs/restworld-input-simple/restworld-input-simple.component.html Renders specific input controls based on the property type and form strategy. ```html @switch(property().type) { @case (PropertyType.Textarea) { @if (useTemplateDrivenForms()){ } @else { } } @case (PropertyType.Date) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.Month) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.Time) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.DatetimeLocal) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.Number) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.Bool) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.DatetimeOffset) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.Duration) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.Image) { @if (useTemplateDrivenForms()) { } @else { } } @case (PropertyType.File) { @if (useTemplateDrivenForms()) { } @else { } } @default { @if (useTemplateDrivenForms()) { } @else { } } } ``` -------------------------------- ### RESTworld Validation Errors HTML Template Source: https://github.com/wertzui/restworld/blob/main/src/RESTworld/RESTworld.Client.Angular/projects/ngx-restworld-client/src/lib/components/restworld-validation-errors/restworld-validation-errors.component.html Displays validation error messages based on property constraints and form errors. ```html @if (property() && property()?.type !== PropertyType.Hidden) { The field '{{prompt()}}' is required. '{{prompt()}}' must be a valid email address. '{{prompt()}}' must be equal or greater than or equal to {{ error.min }}. '{{prompt()}}' must be smaller than or equal to {{ error.max }}. The length of '{{prompt()}}' must be equal or greater than or equal to {{ error.requiredLength }}. The length of '{{prompt()}}' must be shorter than or equal to {{ error.requiredLength }}. The value for '{{prompt()}}' does not match the pattern {{ error }}. {{ error }} } @if (form()) { {{ error }} } ``` -------------------------------- ### Configuring Form Field Requiredness Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md The required status of a form field is determined by the nullability of the ID property. ```csharp // Required in the form — non-nullable id [Display(Name = "Author")] public long AuthorId { get; set; } // Optional in the form — nullable id [Display(Name = "Category")] public long? CategoryId { get; set; } ``` -------------------------------- ### Explicit Foreign Key Override Source: https://github.com/wertzui/restworld/blob/main/doc/dto-types.md Use the ForeignKey attribute when the ID property name does not follow the standard Id suffix convention. ```csharp // BlogIds ends with 's', not 'Id' — the convention cannot resolve it [ForeignKey(nameof(Blogs))] [DisplayName("Blogs")] public required IReadOnlyCollection BlogIds { get; set; } [JsonIgnore] public virtual ICollection? Blogs { get; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.