.NET Angular Stack Snippets
GitHub
Contents
Overview
Back to Top
Snippets are called with the following convention: dna.{snippet}
In this documentation, fillable snippet sections are identified using the same convention they are written with: ${i:default}, where i is the tab-stop index, and default is the default value. I.e. - ${1:Project}.
Available Snippets
.NET Snippets
Back to Top
| Type |
Name |
FileType |
| Entity Class |
dna.ent-class |
C# |
| DbSet Property |
dna.ent-dbset |
C# |
| Entity KeyMap |
dna.ent-keymap |
C# |
| Extension Class |
dna.ext-class |
C# |
| Extension Baseline |
dna.ext-base |
C# |
| Extension Method |
dna.ext-method |
C# |
| Extension Query Method |
dna.ext-query |
C# |
| Extension Get Method |
dna.ext-get |
C# |
| Extension Entity Method |
dna.ext-entity |
C# |
| Extensions Validate Method |
dna.ext-validate |
C# |
| Extensions Validate Async Method |
dna.ext-validate-async |
C# |
| Middleware Class |
dna.mw-class |
C# |
| Middleware Extension |
dna.mw-extension |
C# |
| Controller Class |
dna.ctl-class |
C# |
| Controller Baseline |
dna.ctl-base |
C# |
| Controller Query Method |
dna.ctl-query |
C# |
| Controller Get Method |
dna.ctl-get |
C# |
| Controller Get Method Routed |
dna.ctl-get-routed |
C# |
| Controller Post Method |
dna.ctl-post |
C# |
| Controller Upload Method |
dna.ctl-upload |
C# |
| SignalR Hub Class |
dna.sgl-hub |
C# |
| SignalR Hub Trigger |
dna.sgl-trigger |
C# |
Angular Snippets
Back to Top
| Type |
Name |
FileType |
| Model Interface |
dna.model |
TypeScript |
| Reactive Model Interface |
dna.rx-model |
TypeScript |
| Service Class |
dna.service |
TypeScript |
| Data Source |
dna.source |
TypeScript |
| Service API |
dna.api |
TypeScript |
| Service Http Function |
dna.http-func |
TypeScript |
| Service Http Promise |
dna.http-promise |
TypeScript |
| SignalR Service Class |
dna.sgl-service |
TypeScript |
| SignalR Trigger Function |
dna.sgl-trigger |
TypeScript |
| Component Class |
dna.component |
TypeScript |
| Pipe Class |
dna.pipe |
TypeScript |
| Route Class |
dna.route |
TypeScript |
| Dialog Class |
dna.dialog |
TypeScript |
| Observable Input |
dna.obs-input |
TypeScript |
| Component Index |
dna.idx-component |
TypeScript |
| Dialog Index |
dna.idx-dialog |
TypeScript |
| Model Index |
dna.idx-model |
TypeScript |
| Pipe Index |
dna.idx-pipe |
TypeScript |
| Route Index |
dna.idx-route |
TypeScript |
| Child Route Index |
dna.idx-child-route |
TypeScript |
| Service Index |
dna.idx-service |
TypeScript |
| Drag and Drop Selector |
dna.dnd-selector |
TypeScript |
| Dialog Template |
dna.dialog |
HTML |
| Card Shell |
dna.card-shell |
HTML |
| Flex Container |
dna.flex-container |
HTML |
| Async Container |
dna.async-container |
HTML |
| Loading Template |
dna.loading-template |
HTML |
| Searchbar |
dna.searchbar |
HTML |
| Material Select |
dna.mat-select |
HTML |
| Material Select Change |
dna.mat-select-change |
HTML |
| Material Input |
dna.mat-input |
HTML |
| Material Input Template Reference Variable |
dna.mat-input-trv |
HTML |
| Material Textarea |
dna.mat-textarea |
HTML |
| Material Datepicker |
dna.mat-datepicker |
HTML |
| Material Slider |
dna.mat-slider |
HTML |
| Material Slide Toggle |
dna.mat-slide-toggle |
HTML |
| Material Menu |
dna.mat-menu |
HTML |
| Material Tab Nav |
dna.mat-tab-nav |
HTML |
| Material Route Tab |
dna.mat-route-tab |
HTML |
| Drag and Drop Selector |
dna.dnd-selector |
HTML |
| Reactive Checkbox |
dna.rx-checkbox |
HTML |
| Reactive Datepicker |
dna.rx-datepicker |
HTML |
| Reactive Input |
dna.rx-input |
HTML |
| Reactive Radio |
dna.rx-radio |
HTML |
| Reactive Select |
dna.rx-select |
HTML |
| Reactive Select Change |
dna.rx-select-change |
HTML |
| Reactive Slide Toggle |
dna.rx-slide-toggle |
HTML |
| Reactive Slider |
dna.rx-slider |
HTML |
| Reactive Textarea |
dna.rx-textarea |
HTML |
| Drag and Drop Selector |
dna.dnd-selector |
CSS |
Snippet Anatomy
.NET Snippet Anatomy
Back to Top
Entity Class
dna.ent-class
Parameters:
Project - Project namespace root
Entity - Entity class name
using System;
using System.Collections.Generic;
namespace ${1:Project}.Data.Entities
{
public class ${2:Entity}
{
public int Id { get; set; }
// ENTRY POINT
}
}
DbSet Property
dna.ent-dbset
Parameters:
T - Entity type
Plural - Pluralized entity name
public DbSet<${1:T}> ${2:Plural} { get; set; }
Entity Key Map
dna.ent-keymap
Parameters:
T - Entity type
Many - Many-sided property name
modelBuilder
.Entity<${1:T}>()
.HasMany(x => x.${2:Many})
.WithOne(x => x.${1:T})
.HasForeignKey(x => x.${1:T}Id)
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
Extension Class
dna.ext-class
Parameters:
Project - Project namespace root
Entity - Extension class name
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ${1:Project}.Core;
using ${1:Project}.Core.ApiQuery;
using ${1:Project}.Data.Entities;
namespace ${1:Project}.Data.Extensions
{
public static class ${2:Entity}Extensions
{
// ENTRY POINT
}
}
Extension Class Baseline
dna.ext-base
Parameters:
Project - Project namespace root
Singular - Singular capitalized entity name
plural - Pluralized lowercased entity name
Prop - Initial search property for Search / Validate extension methods
Plural - Pluralized capitalized entity name
singular - Singular lowercased entity name
prop - Lowercased property name for exception message
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ${1:Project}.Core;
using ${1:Project}.Core.ApiQuery;
using ${1:Project}.Data.Entities;
namespace ${1:Project}.Data.Extensions
{
public static class ${2:Singular}Extensions
{
static IQueryable<${2:Singular}> Search(this IQueryable<${2:Singular}> ${3:plural}, string search) =>
${3:plural}.Where(x => x.${4:Prop}.ToLower().Contains(search.ToLower()));
public static async Task<QueryResult<${2:Singular}>> Query${5:Plural}(
this AppDbContext db,
string page,
string pageSize,
string search,
string sort
) {
var container = new QueryContainer<${2:Singular}>(
db.${5:Plural},
page, pageSize, search sort
);
return await container.Query((${3:plural}, s) => ${3:plural}.Search(s));
}
public static async Task<${2:Singular}> Get${2:Singular}(this AppDbCOntext db, int id) =>
await db.${5:Plural}
.FindAsync(id);
public static async Task Add${2:Singular}(this AppDbCOntext db, ${2:Singular} ${6:singular})
{
if (await ${6:singular}.Validate(db))
{
await db.${5:Plural}.AddAsync(${6:singular});
await db.SaveChangesAsync();
}
}
public static async Task Update${2:Singular}(this AppDbContext db, ${2:Singular} ${6:singular})
{
if (await ${6:singular}.Validate(db))
{
db.${5.Plural}.Update(${6:singular});
await db.SaveChangesAsync();
}
}
public static async Task Remove${2:Singular}(this AppDbContext db, ${2:Singular} ${6:singular})
{
db.${5:Plural}.Remove(${6:singular});
await db.SaveChangesAsync();
}
static async Task<bool> Validate(this ${2:Singular} ${6:singular}, AppDbContext db)
{
if (string.IsNullOrEmpty(${6:singular}.${4:Prop}))
{
throw new AppException("${2:Singular} must have a ${7:prop}", ExceptionType.Validation);
}
var check = await db.${5:Plural}
.FirstOrDefaultAsync(x =>
x.Id != ${6:singular}.Id &&
x.${4:Prop}.ToLower() == ${6:singular}.${4:Prop}.ToLower()
);
if (check != null)
{
throw new AppException($"{${6:singular}.${4:Prop}} is already a ${2:Singular}", ExceptionType.Validation);
}
return true;
}
// ENTRY POINT
}
}
Extension Method
dna.ext-method
Parameters:
Method - Extension method name
T - Extension method argument type
arg - Extension method name
public static async Task ${1:Method}(this ${2:T} ${3:arg})
{
// ENTRY POINT
}
Extension Query Method
dna.ext-query
Parameters:
T: Entity type
Plural: Pluralized capitalized entity name
plural: Pluralized lowercase entity name
public static async Task<QueryResult<${1:T}>> Query${2:Plural}(
this AppDbContext db,
string page,
string pageSize,
string search,
string sort
) {
var container = new QueryContainer<${1:T}>(
db.${2:Plural}/* ENTRY POINT */,
page, pageSize, search, sort
);
return await container.Query((${3:plural}, s) => ${3:plural}.Search(s));
}
Extension Get Method
dna.ext-get
Parameters:
T - Extension method return type
Get - Extension method name
public static async Task<${1:T}> ${2:Get}(this AppDbContext db)
{
// ENTRY POINT
}
Extension Entity Method
dna.ext-entity
Parameters:
Method - Extension method name
T - Extension method argument type
arg - Extension method argument name
public static async Task ${1:Method}(this AppDbContext db, ${2:T} ${3:arg})
{
// ENTRY POINT
}
Extension Validate Method
dna.ext-validate
Parameters:
T - Extension method argument type
arg - Extension method argument name
public static bool Validate(this ${1:T} ${2:arg})
{
// ENTRY POINT
return true;
}
Extension Validate Async Method
dna.ext-validate-async
Parameters:
T - Extension method argument type
arg - Extension method argument name
public static async Task<bool> Validate(this ${1:T} ${2:arg}, AppDbContext db)
{
// ENTRY POINT
return true;
}
Middleware Class
dna.mw-class
Parameters:
Namespace - Namespace for this class
App - Middleware class name
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace ${1:Namespace}
{
public class ${2:App}Middleware
{
private readonly RequestDelegate next;
public ${2:App}Middleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
// ENTRY POINT
await next(context);
}
}
}
Middleware Extension
dna.mw-extension
Parameters:
Namespace - Namespace containing middleware class
Middleware - Extension class name
App - Middleware class name
using ${1:Namespace};
namespace Microsoft.AspNetCore.Builder
{
public static class ${2:Middleware}Extensions
{
public static IApplicationBuilder Use${3:App}Middleware(this IApplicationBuilder builder) =>
builder.UseMiddleware<${3:App}Middleware>();
}
}
Controller Class
dna.ctl-class
Parameters:
Project - Project namespace name
Entity - Controller name
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ${1:Project}.Data;
using ${1:Project}.Data.Entities;
using ${1:Project}.Data.Extensions;
namespace ${1:Project}.Web.Controllers
{
[Route("api/[controller]")]
public class ${2:Entity}Controller : Controller
{
private AppDbContext db;
public ${2:Entity}Controller(AppDbContext db)
{
this.db = db;
}
// ENTRY POINT
}
}
Controller Class Baseline
dna.ctl-base
Parameters:
Project - Project namespace name
Singular - Singular capitalized entity name
Plural - Pluralized capitalized entity name
singular - Singular lowercase entity name
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ${1:Project}.Data;
using ${1:Project}.Data.Entities;
using ${1:Project}.Data.Extensions;
namespace ${1:Project}.Web.Controllers
{
[Route("api/[controller]")]
public class ${2:Singular}Controller : Controller
{
private AppDbContext db;
public ${2:Singular}Controller(AppDbContext db)
{
this.db = db;
}
[HttpGet("[action]")]
[ProducesResponseType(typeof(QueryResult<${2:Singular}>), 200)]
public async Task<IActionResult> Query${3:Plural}(
[FromQuery]string page,
[FromQuery]string pageSize,
[FromQuery]string search,
[FromQuery]string sort
) => Ok(await db.Query${3:Plural}(page, pageSize, search, sort));
[HttpGet("[action]/{id}")]
public async Task<${2:Singular}> Get${2:Singular}([FromRoute]int id) => await db.Get${2:Singular}(id);
[HttpPost("[action]")]
public async Task Add${2:Singular}([FromBody]${2:Singular} ${4:singular}) => await db.Add${2:Singular}(${4:singular});
[HttpPost("[action]")]
public async Task Update${2:Singular}([FromBody]${2:Singular} ${4:singular}) => await db.Update${2:Singular}(${4:singular});
[HttpPost("[action]")]
public async Task Remove${2:Singular}([FromBody]${2:Singular} ${4:singular}) => await db.Remove${2:Singular}(${4:singular});
}
}
Controller Get Method
dna.ctl-get
Parameters:
T - Action method return type
Get - Action method name
[HttpGet("[action]")]
public async Task<${1:T}> ${2:Get}() => await db.${2:Get}();
Controller Get Method Routed
dna.ctl-get-routed
Parameters:
param - Routed parameter name
T - Action method return type
Get - Action method name
type - Type of the routed parameter
[HttpGet("[action]/{${1:param}}")]
public async Task<${2:T}> ${3:Get}([FromRoute]${4:type} ${1:param}) => await db.${3:Get}(${1:param});
Controller Post Method
dna.ctl-post
Parameters:
Post - Action method name
T - Action method argument type
arg - Action method argument name
[HttpPost("[action]")]
public async Task METHOD([FromBody]TYPE ARG) => await db.METHOD(ARG);
Controller Upload Method
dna.ctl-upload
Parameters:
T - Method return type
Upload - Method name
[HttpPost("[action]")]
[DisableRequestSizeLimit]
public async Task<${1:T}> ${2:Upload}() =>
await db.${2:Upload}(
Request.Form.Files,
config.DirectoryBasePath,
config.UrlBasePath
);
SignalR Hub Class
dna.sgl-hub
Parameters:
Project - Project namespace name
Data - Hub name
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
namespace ${1:Project}.Web.Hubs
{
public class ${2:Data}Hub : Hub
{
// ENTRY POINT
}
}
SignalR Hub Trigger
dna.sgl-trigger
Parameters:
Action - Trigger function name, PascalCase
T - Function argument type
arg - Function argument name
SendAll - Clients method to execute
method - Client function to execute
public async Task trigger${1:Action}(${2:T} ${3:arg})
{
await Clients.${4:SendAll}(${3:arg}).SendAsync("${5:method}");
}
Angular Snippet Anatomy
Back to Top
Model Interface
dna.model
Parameters:
Model - Interface name, PascalCase
export interface ${1:Model} {
id: number;
// ENTRY POINT
}
Reactive Model Interface
dna.rx-model
Parameters:
Model - Interface name, PascalCase
model - Interface name, camelCase
import {
FormBuilder,
FormGroup,
Validators
} from '@angular/forms';
export interface ${1:Model} {
id: number,
$0
}
export const ${1:Model}Form = (${2:model}: ${1:Model}, fb: FormBuilder): FormGroup =>
fb.group({
id: ${2:model}.id
})
Service Class
dna.service
Parameters:
../../services - Relative path to library services module
../../config - Relative path to library config module
T - Entity type or service name
import {
Injectable,
Optional
} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { SnackerService } from '${1:../../services}';
import { ServerConfig } from '${2:../../config}';
@Injectable()
export class ${3:T}Service {
// ENTRY POINT
constructor(
private http: HttpClient,
private snacker: SnackerService,
@Optional() private config: ServerConfig
) { }
}
Data Source
dna.source
Parameters:
Singular - Entity type capitalized
$2 - Property names to render in a table. Provided as strings in columns array.
prop - Default sorting property name
singular - Entity type lowercase
Plural - Pluralized entity name capitalized
import {
Injectable,
Optional
} from '@angular/core';
import {
QueryService,
SnackerService
} from '../../../services';
import { DataSource } from '@angular/cdk/table';
import { HttpClient } from '@angular/common/http';
import { ServerConfig } from '../../../config';
import { ${1:Singular} } from '../../models';
@Injectable()
export class ${1:Singular}Source extends QueryService<${1:Singular}> implements DataSource<${1:Singular}> {
columns = [$2];
constructor(
protected http: HttpClient,
protected snacker: SnackerService,
@Optional() private config: ServerConfig
) {
super(http, snacker);
this.sort = {
isDescending: false,
propertyName: '${3:prop}'
};
this.baseUrl = `${this.config.api}${4:singular}/query${5:Plural}`;
}
track${5:Plural} = (${4:singular}: ${1:Singular}) => ${4:singular}.id;
}
Service API
dna.api
Parameters:
Singular - Entity name capitalized
singular - Entity name lowercase
id - Parameter to allow id to be interpolated in the http.get string
prop - Property used in success messages
import {
Injectable,
Optional
} from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject} from 'rxjs';
import { SnackerService } from '../../services';
import { ServerConfig } from '../../config';
import { ${1:Singular} } from '../models';
@Injectable()
export class ${1:Singular}Service {
private ${2:singular} = new BehaviorSubject<${1:Singular}>(null);
${2:singular}$ = this.${2:singular}.asObservable();
constructor(
private http: HttpClient,
private snacker: SnackerService,
@Optional() private config: ServerConfig
) { }
get${1:Singular} = (id: number): Promise<${1:Singular}> => new Promise((resolve) => {
this.http.get<${1:Singular}>(`${this.config.api}${2:singular}/get${1:Singular}/${3:id}`)
.subscribe(
data => {
this.${2:singular}.next(data);
resolve(data);
},
err => {
this.snacker.sendErrorMessage(err.error);
resolve(null);
}
);
})
add${1:Singular} = (${2:singular}: ${1:Singular}): Promise<boolean> => new Promise((resolve) => {
this.http.post(`${this.config.api}${2:singular}/add${1:Singular}`, ${2:singular})
.subscribe(
() => {
this.snacker.sendSuccessMessage(`${${2:singular}.${4.prop}} successfully created`);
resolve(true);
},
err => {
this.snacker.sendErrorMessage(err.error);
resolve(false);
}
);
})
update${1:Singular} = (${2:singular}: ${1:Singular}): Promise<boolean> => new Promise((resolve) => {
this.http.post(`${this.config.api}${2:singular}/update${1:Singular}`, ${2:singular})
.subscribe(
() => {
this.snacker.sendSuccessMessage(`${${2:singular}.${4:prop}} successfully updated`);
resolve(true);
},
err => {
this.snacker.sendErrorMessage(err.error);
resolve(false);
}
);
})
remove${1:Singular} = (${2:singular}: ${1:Singular}): Promise<boolean> => new Promise((resolve) => {
this.http.post(`${this.config.api}${2:singular}/remove${1:Singular}`, ${2:singular})
.subscribe(
() => {
this.snacker.sendSuccessMessage(`${${2:singular}.${4:prop}} successfully removed`);
resolve(true);
},
err => {
this.snacker.sendErrorMessage(err.error);
resolve(false);
}
);
})
}
Service HTTP Function
dna.http-func
Parameters:
func - Function name, camelCase
T - Return value type
route - API route
stream - Stream to populate when data returns
${1:func} = () => this.http.get<${2:T}>(`/api/${3:route}`)
.subscribe(
data => this.${4:stream}.next(data),
err => this.snacker.sendErrorMessage(err.error)
);
Service HTTP Promise
dna.http-promise
Parameters:
func - Function name, camelCase
arg - Function argument name
T - Function argument type
post - HTTP function
route - API route
success - Message to send on success
${1:func} = (${2:arg}: ${3:T}): Promise<boolean> =>
new Promise((resolve) => {
this.http.${4:post}(`/api/${5:route}`, ${2:arg})
.subscribe(
() => {
this.snacker.sendSuccessMessage(${6:success});
// ENTRY POINT
resolve(true);
},
err => {
this.snacker.sendErrorMessage(err.error);
resolve(false);
}
)
});
SignalR Service Class
dna.sgl-service
Parameters:
Object - Service name, PascalCase
url - Hub route URL
import { Injectable } from '@angular/core';
import { HubConnectionBuilder } from '@aspnet/signalr';
import { BehaviorSubject } from 'rxjs';
import { SnackerService } from '../snacker.service';
@Injectable()
export class ${1:Object}SocketService {
private connection = new HubConnectionBuilder()
.withUrl('${2:url}')
.build();
private connected = new BehaviorSubject<boolean>(false);
private error = new BehaviorSubject<any>(null);
private trigger = new BehaviorSubject<boolean>(false);
connected$ = this.connected.asObservable();
error$ = this.error.asObservable();
trigger$ = this.trigger.asObservable();
constructor(
private snacker: SnackerService
) {
this.connection
.start()
.then(() => this.connected.next(true))
.catch((err) => {
this.connected.next(false);
this.error.next(err);
this.snacker.sendErrorMessage(err.error);
});
}
// ENTRY POINT
}
SignalR Trigger Function
dna.sgl-trigger
Parameters:
Func - Trigger function name, postfixed to trigger
arg - Trigger function argument name
T - Trigger function argument type
trigger${1:Func} = async (${2:arg}: ${3:T}) =>
this.connected.value &&
await this.connection
.invoke('trigger${1:Func}', ${2:arg});
Component Class
dna.component
Parameters:
Model - Model dependency to import
selector - Component selector, kebab-case
Object - Component name, PascalCase
import {
Component,
Input,
Output,
EventEmitter
} from '@angular/core';
import { ${1:Model} } from '../../models';
@Component({
selector: '${2:selector}',
templateUrl: '${2:selector}.component.html'
})
export class ${3:Object}Component {
// ENTRY POINT
}
Pipe Class
dna.pipe
Parameters:
name - Pipe name, camelCase
Object - Pipe name, PascalCase
T - Type for value
args - Additional pipe arguments
import {
Pipe,
PipeTransform
} from '@angular/core';
@Pipe({
name: '${1:name}'
})
export class ${2:Object}Pipe implements PipeTransform {
transform(value: ${3:T}, ${4:args}) {
// ENTRY POINT
}
}
Route Class
dna.route
Parameters:
object - Route short name, kebab-case
Object - Route short name, PascalCase
import {
Component,
OnInit
} from '@angular/core';
@Component({
selector: '${1:object}-route',
templateUrl: '${1:object}.component.html'
})
export class ${2:Object}Component implements OnInit {
constructor(
) { }
ngOnInit() {
}
// ENTRY POINT
}
Dialog Class
dna.dialog
Parameters:
object - Dialog short name, kebab-case
Object - Dialog short name, PascalCase
import {
MatDialogRef,
MAT_DIALOG_DATA
} from '@angular/material';
import {
Component,
Inject
} from '@angular/core';
@Component({
selector: '${1:object}-dialog',
templateUrl: '${1:object}.dialog.html'
})
export class ${2:Object}Dialog {
// ENTRY POINT
}
Observable Input
dna.obs-input
Parameters:
inputVar - Template reference variable name
false - Set the value of @ViewChild's static option
Assumes a local initialized variable, as well as injecting a core: CoreService object that contains a generateInputObservable(input: ElementRef) function. See StackBlitz - Docs ViewChild for an example.
@ViewChild('${1:inputVar}', { static: ${2: false} })
set ${1:inputVar}(input: ElementRef) {
if (input && !this.initialized) {
this.core.generateInputObservable(input)
.subscribe(async val => {
// ENTRY POINT
});
this.initialized = true;
}
}
Component Index
dna.idx-component
Parameters:
Component - The initial component to load
dir - The relative path to the initial component file
Object - The name of the exported components array
import { ${1:Component} } from '${2:dir}';
// ENTRY POINT
export const ${3:Object}Components = [
${1:Component}
];
Dialog Index
dna.idx-dialog
Parameters:
Dialog - The initial dialog to load
dir - The relative path to the initial dialog file
Object - The name of the exported dialogs array
import { ${1:Dialog} } from '${2:dir}';
// ENTRY POINT
export const ${3:Object}Dialogs = [
${1:Dialog}
];
export * from '${2:dir}';
Model Index
dna.idx-model
No parameters
export * from '$0'; // ENTRY POINT @ $0
Pipe Index
dna.idx-pipe
Parameters:
Pipe - The initial pipe to load
dir - The relative path to the initial pipe file
Object - The name of the exported pipes array
import { ${1:Pipe} } from '${2:dir}';
// ENTRY POINT
export const ${3:Object}Pipes = [
${1:Pipe}
];
Route Index
dna.idx-route
Parameters:
RouteComponent - The initial route component to load
dir - The relative path to the initial route component file
path - The route the initial route component should resolve to
import { Route } from '@angular/router';
import { ${1:RouteComponent} } from '${2:dir}';
// ENTRY POINT
export const RouteComponents = [
${1:RouteComponent}
];
export const Routes: Route[] = [
{ path: '${3:path}', component: ${1:RouteComponent} },
{ path: '', redirectTo: '${3:path}', pathMatch: 'full' },
{ path: '**', redirectTo: '${3:path}', pathMatch: 'full' }
];
Child Route Index
dna.idx-child-route
Parameters:
RouteComponent - The initial route component to load
dir - The relative path to the initial route component file
Object - The name of the exported routes / route components arrays
path - The route the initial route component should resolve to
import { Route } from '@angular/router';
import { ${1:RouteComponent} } from '${2:dir}';
// ENTRY POINT
export const ${3:Object}Components = [
${1:RouteComponent}
];
export const ${3:Object}Routes: Route[] = [
{ path: '${4:path}', component: ${1:RouteComponent} },
{ path: '', redirectTo: '${4:path}', pathMatch: 'prefix' },
{ path: '**', redirectTo: '${4:path}', pathMatch: 'prefix' }
];
Service Index
dna.idx-service
Parameters:
Service - The initial service to load
dir - The relative path to the initial service file
Any service loaded into the Services array is added to a ServicesModule.providers array, thus making it initially globally scoped.
import { ${1:Service} } from '${2:dir}';
// ENTRY POINT
export const Services = [
${1:Service}
];
export * from '${2:dir}';
Drag and Drop Selector
dna.dnd-selector
Parameters:
Object - Interface of the object represented by drag and drop arrays and Selector component name, PascalCased
object - Name for the selector component, kebab-cased
available - Array of objects available to add
assigned - Array of objects currently assigned and can be removed by dragging to available
import {
Component,
Input,
Output,
EventEmitter
} from '@angular/core';
import {
CdkDragDrop,
moveItemInArray,
transferArrayItem
} from '@angular/cdk/drag-drop';
import { ${1:Object} } from '../../models';
@Component({
selector: '${2:object}-selector',
templateUrl: '${2:object}-selector.component.html',
styleUrls: ['${2:object}-selector.component.css']
})
export class ${1:Object}SelectorComponent {
@Input() ${3:available}: ${1:Object}[];
@Input() ${4:assigned}: ${1:Object}[];
@Input() pending = false;
@Output() save = new EventEmitter<${1:Object}[]>();
drop = (event: CdkDragDrop<${1:Object}[]>) => {
event.previousContainer !== event.container ?
transferArrayItem(
event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex
) :
moveItemInArray(
event.container.data,
event.previousIndex,
event.currentIndex
);
}
}
Angular Template Anatomy
Back to Top
Dialog Template
dna.dialog
Parameters:
title - Dialog title text
<div class="mat-typography">
<h2 mat-dialog-title>${1:title}</h2>
<mat-dialog-content>
<!-- ENTRY POINT -->
</mat-dialog-content>
<mat-dialog-actions>
<button mat-stroked-button
mat-dialog-close>Cancel</button>
</mat-dialog-actions>
</div>
Card Shell
dna.card-shell
Parameters:
background card elevated - Classes
layout - Flex layout
align - Flex layout alignment
<section class="${1:background card elevated}"
fxLayout="${2:layout}"
fxLayoutAlign="${3:align}">
<!-- ENTRY POINT -->
</section>
Flex Container
dna.flex-container
Parameters:
layout - Flex layout
align - Flex layout alignment
container - Classes
<section fxLayout="${1:layout}"
fxLayoutAlign="${2:align}"
class="${3:container}">
<!-- ENTRY POINT -->
</section>
Async Container
dna.async-container
Parameters:
stream - Stream to subscribe to with the async pipe
data - Resulting value of data resolved by stream
loading - Loading template name
<ng-container *ngIf="${1:stream} | async as ${2:data} else ${3:loading}">
<!-- ENTRY POINT -->
</ng-container>
Loading Template
dna.loading-template
Parameters:
loading - Loading template reference variable name
indeterminate - MatProgressBar mode
accent - Material theme color
<ng-template #${1:loading}>
<mat-progress-bar mode="${2:indeterminate}"
color="${3:accent}"></mat-progress-bar>
</ng-template>
Searchbar
dna.searchbar
Parameters:
Search - Searchbar label
1 - Minimum number of characters before search function is executed
searchFunction - Function to execute on search
clearFunction - Function to execute when searchbar is cleared
Assumes a SearchbarComponent exists with the specified input / output properties. See StackBlitz - Searchbar Example for this component definition and implementation.
<searchbar label="${1:Search}"
[minimum]="${2:1}"
(search)="${3:searchFunction}"
(clear)="${4:clearFunction}"></searchbar>
Material Select
dna.mat-select
Parameters:
Label - Select label
model - Data binding target for the select element
o - Variable representing a single option from a collection of objects
data - A collection of objects to iterate when populating select options
val - Value that a select option represents from the o variable
display - Property / Properties to display from the o variable in the select list
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<mat-select [(ngModel)]="${2:model}">
<mat-option *ngFor="let ${3:o} of ${4:data}"
[value]="${5:val}">
{{${6:display}}}
</mat-option>
</mat-select>
</mat-form-field>
Material Select Change
dna.mat-select-change
Parameters:
Label - Select label
model - Data binding target for the select element
changeFunction() - Function to execute when the selection is changed
o - Variable representing a single option from a collection of objects
data - A collection of objects to iterate when populating select options
val - Value that a select option represents from the o variable
display - Property / Properties to display from the o variable in the select list
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<mat-select [(ngModel)]="${2:model}"
(selectionChange)="${3:changeFunction()}">
<mat-option *ngFor="let ${4:o} of ${5:data}"
[value]="${6:val}">
{{${7:display}}}
</mat-option>
</mat-select>
</mat-form-field>
Material Input
dna.mat-input
Parameters:
Label - Input label
model - Data binding target for the input element
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<input matInput
[(ngModel)]="${2:model}" />
</mat-form-field>
Material Input Template Reference Variable
dna.mat-input-trv
Parameters:
Label - Input label
model - Data binding target for the input element
varName - Template reference variable name
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<input matInput
[(ngModel)]="${2:model}"
#${3:varName} />
</mat-form-field>
Material Textarea
dna.mat-textarea
Parameters:
Label - Textarea label
model - Data binding target for the textarea element
4 - Autosize minimum rows
8 - Autosize maximum rows
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<textarea matInput
mat-autosize
[(ngModel)]="${2:model}"
[matAutosizeMinRows]="${3:4}"
[matAutosizeMaxRows]="${4:8}"></textarea>
</mat-form-field>
Material Datepicker
dna.mat-datepicker
Parameters:
Label - Input label
model - Data binding target for the input element
picker - Template Reference Variable name for mat-datepicker
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<input [(ngModel)]="${2:model}" matInput [matDatepicker]="${3:picker}">
<mat-datepicker-toggle matSuffix [for]="${3:picker}"></mat-datepicker-toggle>
<mat-datepicker #${3:picker}></mat-datepicker>
</mat-form-field>
Material Slider
dna.mat-slider
Parameters:
model - Data binding target for the mat-slider element
min - Minimum value
max - Maximum value
true - Show thumblabel
step - Step values at which thumb will snap
interval - How often ticks are shown on the slider
inputFunction - Function to call as slider values change
<mat-slider [value]="${1:model}"
[min]="${2:min}"
[max]="${3:max}"
[thumbLabel]="${4:true}"
[step]="${5:step}"
[tickInterval]="${6:interval}"
(input)="${7:inputFunction}"></mat-slider>
Material Slide Toggle
dna.mat-slide-toggle
Parameters:
model - Data binding target for the mat-slide-toggle element
Label - Slide toggle label
<mat-slide-toggle [(ngModel)]="${1:model}">${2:Label}</mat-slide-toggle>
Material Menu
dna.mat-menu
Parameters:
menu - Menu reference variable name
menu_icon - Material icon to use for the menu button
clickFunction - Function to execute for the initial menu item
label - Initial menu item label
<button mat-icon-button
[matMenuTriggerFor]="${1:menu}">
<mat-icon>${2:menu_icon}</mat-icon>
</button>
<mat-menu #${1:menu}="matMenu">
<button mat-menu-item
(click)="${3:clickFunction}">
{{${4:label}}}
</button>
<!-- ENTRY POINT -->
</mat-menu>
Material Tab Nav
dna.mat-tab-nav
Parameters:
scrolled - Additional class to apply to the nav element.
link - Initial tab link
active - Initial tab active class
Label - Initial tab label
<nav mat-tab-nav-bar
class="${1:scrolled}">
<a mat-tab-link
routerLink="${2:link}"
routerLinkActive="${3:active}">
${4:Label}
</a>
<!-- ENTRY POINT -->
</nav>
<router-outlet></router-outlet>
Material Route Tab
dna.mat-route-tab
Parameters:
link - Initial tab link
active - Initial tab active class
Label - Initial tab label
<a mat-tab-link
routerLInk="${1:link}"
routerLinkActive="${3:active}">
${3:Label}
</a>
Drag and Drop Selector
dna.dnd-selector
Parameters:
Available - Label for objects available for adding
available - Collection of objects available for adding
x - Variable to represent a singular object from available in an iteration
x.display - Property to display for x
Assigned - Label for objects already added / available for removal
assigned - Collection of objects already added / available for removal
y - Variable to represent a singular object from assigned in an iteration
y.display - Property to display for y
<button mat-stroked-button
[disabled]="pending"
[style.margin.px]="8"
(click)="save.emit(${6:assigned})">
Save
</button>
<section fxLayout="row"
fxLayoutAlign="start start">
<section fxLayout="column"
fxLayoutAlign="start stretch"
fxFlex
[style.margin-right.px]="4">
<p class="mat-title">${1:Available}</p>
<section cdkDropList
#list="cdkDropList"
[cdkDropListData]="${2:available}"
[cdkDropListConnectedTo]="[selected]"
class="container drop-container"
(cdkDropListDropped)="drop($event)">
<section *ngFor="let ${3:x} of ${2:available}"
class="background card container elevated clickable"
cdkDrag">
<div class="drag-placeholder" *cdkDragPlaceholder></div>
<p>{{${4:x.display}}}</p>
</section>
</section>
</section>
<section fxLayout="column"
fxLayoutAlign="start stretch"
fxFlex
[style.margin-right.px]="4">
<p class="mat-title">${5:Assigned}</p>
<section cdkDropList
#selected="cdkDropList"
[cdkDropListData]="${6:assigned}"
[cdkDropListConnectedTo]="[list]"
class="container drop-container"
(cdkDropListDropped)="drop($event)">
<section *ngFor="let ${7:y} of ${6:assigned}"
class="background card container elevated clickable"
cdkDrag">
<div class="drag-placeholder" *cdkDragPlaceholder></div>
<p>{{${8:y.display}}}</p>
</section>
</section>
</section>
</section>
Reactive Checkbox
dna.rx-checkbox
Parameters:
control - Form control name
true - Indeterminate value
Label - Checkbox label
<mat-checkbox formControlName="${1:control}" [indeterminate]="${2:true}">${3:Label}</mat-checkbox>
Reactive Datepicker
dna.rx-datepicker
Parameters:
Label - Input label
control - Form control name
picker - Template Reference Variable for mat-datepicker
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<input formControlName="${2:control}" matInput [matDatepicker]="${3:picker}">
<mat-datepicker-toggle matSuffix [for]="${3:picker}"></mat-datepicker-toggle>
<mat-datepicker #${3:picker}></mat-datepicker>
</mat-form-field>
Reactive Input
dna.rx-input
Parameters:
Label - Input label
control - Form control name
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<input formControlName="${2:control}" matInput>
</mat-form-field>
Reactive Radio
dna.rx-radio
Parameters:
Label - Input label
control - Form control name
column - Flex layout alignment (column or row)
8px - Flex layout gap
opt - Iteration variable for the data collection
data - Collection of data for the radio group
value - Value each radio button represents
display - Value to display for each radio button label
<label>${1:Label}</label>
<mat-radio-group formControlName="${2:control}" fxLayout="${3:column}" fxLayoutGap="${4:8px}">
<mat-radio-button *ngFor="let ${5:opt} of ${6:data}" [value]="${7:value}">{{${8:display}}}</mat-radio-button>
</mat-radio-group>
Reactive Select
dna.rx-select
Parameters:
Label - Input label
control - Form control name
opt - Iteration variable for the data collection
data - Collection of data for the select options
value - Value each option represents
display - Value to display for each option
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<mat-select formControlName="${2:control}">
<mat-option *ngFor="let ${3:opt} of ${4:data}" [value]="${5:value}">{{${6:display}}}</mat-option>
</mat-select>
</mat-form-field>
Reactive Select Change
dna.rx-select-change
Parameters:
Label - Input label
control - Form control name
changeFunction() - Function to execute when the selection is changed
opt - Iteration variable for the data collection
data - Collection of data for the select options
value - Value each option represents
display - Value to display for each option
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<mat-select formControlName="${2:control}" (selectionChange)="${3:changeFunction()}">
<mat-option *ngFor="let ${4:opt} of ${5:data}" [value]="${6:value}">{{${7:display}}}</mat-option>
</mat-select>
</mat-form-field>
Reactive Slide Toggle
dna.rx-slide-toggle
Parameters:
control - Form control name
Label - Slide toggle label
<mat-slide-toggle formControlName="${1:control}">${2:Label}</mat-slide-toggle>
Reactive Slider
dna.rx-slider
Parameters:
8px - Layout gap for contained elements
Label - Slider label
control - Form control name
min - Slider min value
max - Slider max value
step - Interval of change
true - Whether or not to render the thumb label
value - Property that displays the value held by the slider
<section fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="${1:8px}">
<span>${2:Label}</span>
<mat-slider formControlName="${3:control}" [min]="${4:min}" [max]="${5:max}" [step]="${6:step}" [thumbLabel]="${7:true}"></mat-slider>
<span>{{${8:value}}}</span>
</section>
Reactive Textarea
dna.rx-textarea
Parameters:
Label - Textarea label
control - Form control name
min - Min textarea rows
max - Max textarea rows
<mat-form-field>
<mat-label>${1:Label}</mat-label>
<textarea formControlName="${2:control}" matInput mat-autosize [matAutosizeMinRows]="${3:min}" [matAutosizeMaxRows]="${4:max}"></textarea>
</mat-form-field>
Angular Style Anatomy
Back to Top
Drag and Drop Selector
dna.dnd-selector
No parameters
.drop-container {
border: dotted 1px #ccc;
min-height: 50px;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.drop-container.cdk-drop-list-dragging .drop-container:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.drag-placeholder {
background: #eee;
border: dotted 1px #ccc;
min-height: 50px;
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.cdk-drag-preview {
font-family: 'Roboto';
background-color: rgba(247, 247, 247, .7);
}