Skip to content

Fireside Chats to stay with us beyond ngAtlanta

We Hosted Diversity Chats and You Should Too- What We Learned

1*QO Iaxa9wyDXhNcLHFyA-A

There’s something about diversity that brings everyone together. Whether you’re interested in developing a more inclusive team or you already live and breathe it, discussions about equality can spark opportunities and build communities.

Some of the Angular community’s most talented speakers came together to discuss diversity and inclusion at January’s This Dot Fireside Chats during the ngAtlanta conference, in partnership with the Paved Road Foundation.

The consensus was clear: Changing the gender ratio in the tech workplace is paramount. Together, speakers and attendees were able to dive deep and get personal, while remaining judgment-free, on all things diversity and inclusion.

One of the liveliest hot-button topics we discussed was, “What does it mean to stand up for yourself?”

Speakers and audience members chimed in with their thoughts on what it means to value yourself and expect dignity and respect.

The biggest takeaway? “No matter your level, you still have valuable input.”

It’s more important than ever to encourage everyone to get involved at every level. Developers coming from various non-traditional backgrounds are learning to code and hungry to participate in the growing tech market.

But without mentorship and guidance, new developers will never be able to ramp up to their full potential. Everyone has something to offer the tech world. But first, we must set an example by creating a level playing field.

We strongly encourage all organizers to host Diversity Chats at their conferences! Create conversations around inclusion and the new workforce that’s starting to enter the tech world.

Have questions? Email us about our experiences! hi@thisdot.co.

This post was written by Kaelyn St James, a team member at This Dot Labs.

This Dot Labs is a development consultancy that is trusted by top industry companies, including Stripe, Xero, Wikimedia, Docusign, and Twilio. This Dot takes a hands-on approach by providing tailored development strategies to help you approach your most pressing challenges with clarity and confidence. Whether it's bridging the gap between business and technology or modernizing legacy systems, you’ll find a breadth of experience and knowledge you need. Check out how This Dot Labs can empower your tech journey.

You might also like

A Guide to Custom Angular Attribute Directives cover image

A Guide to Custom Angular Attribute Directives

When working inside of Angular applications you may have noticed special attributes such as NgClass, NgStyle and NgModel. These are special attributes that you can add to elements and components that are known as attribute directives. In this article, I will cover how these attributes are created and show a couple of examples. What are Attribute Directives? Angular directives are special constructs that allow modification of HTML elements and components. Attribute directives are also applied through attributes, hence the name. There exist other types of directives such as structural directives as well, but we’re just going to focus on attribute directives. If you’ve used Angular before then you have almost certainly used a couple of the attribute directives I mentioned earlier before. You are not limited to just the built-in directives though. Angular allows you to create your own! Creating Attribute Directives Directives can be created using code generation via the ng CLI tool. ` ng generate directive ` This will create a file to house your directive and also an accompanying test file as well. The contents of the directive are very barebones to start with. Let’s take a look. ` import { Directive } from '@angular/core'; @Directive({ selector: '[appExample]', }) export class ExampleDirective { constructor() {} } ` You will see here that directives are created using a @Directive decorator. The selector in this case is the name of the attribute as it is intended to be used in your templates. The square brackets around the name make it an attribute selector, which is what we want for a custom attribute directive. I would also recommend that a prefix is always used for directive names to minimize the risk of conflicts. It should also go without saying to avoid using the ng prefix for custom directives to avoid confusion. Now, let’s go over the lifecycle of a directive. The constructor is called with a reference to the ElementRef that the directive was bound to. You can do any initialization here if needed. This element reference is dependency injected, and will be available outside the constructor as well. You can also set up @HostListener handlers if you need to add functionality that runs in response to user interaction with the element or component, and @Input properties if you need to pass data to the directive. Click Away Directive One useful directive that doesn’t come standard is a click away directive. This is one that I have used before in my projects, and is very easy to understand. This directive uses host listeners to listen for user input, and determine whether the element that directive is attached to should be visible or not after the click event occurs. ` @Directive({ selector: '[appClickAway]', }) export class ClickAwayDirective { @Output() onClickAway: EventEmitter = new EventEmitter(); constructor(private elementRef: ElementRef) {} @HostListener('document:click', ['$event']) onClick(event: PointerEvent): void { if (!this.elementRef.nativeElement.contains(event.target)) { this.onClickAway.emit(event); } } } ` There are a few new things in this directive we’ll briefly go over. The first thing is the event emitter output onClickAway. A generic directive isn’t going to know how to handle click away behavior by itself as this will change based on your use case when using the directive. To solve this issue, we make the directive emit an event that the user of the directive can listen for. The other part is the click handler. We use @HostListener to attach a click handler so we can run our click away logic whenever clicks are done. The one interesting thing about this directive is that it listens to all click events since we’ve specified ‘document’ in the first parameter. The reason for this is because we care about listening for clicking anything that isn’t the element or component that the directive is attached to. If we didn’t do this, then the event handler would only fire when clicking on the component the directive is attached to, which defeats the purpose of a click away handler. Once we’ve determined the element was not clicked, we emit the aforementioned event. Using this directive makes it trivial to implement click away functionality for both modals and context menus alike. If we have a custom dialog component we could hook it up like this: ` Dialog Box This is a paragraph with content! ` If you want to see this directive in action, then you can find it in our blog demos repo here. Drag and Drop Directive Another useful directive is one that assists with drag and drop operations. The following directive makes elements draggable, and executes a function with a reference to the location where the element was dragged to. ` @Directive({ selector: '[appDragDrop]', }) export class DragDropDirective implements OnInit, OnDestroy { @Output() onDragDrop: EventEmitter = new EventEmitter(); mouseDown$ = new Subject(); mouseUp$ = new Subject(); destroy$ = new Subject(); constructor(private elementRef: ElementRef) {} ngOnInit(): void { this.mouseDown$ .pipe(takeUntil(this.destroy$)) .pipe(exhaustMap(() => this.mouseUp$.pipe(take(1)))) .subscribe((event) => { if ( event.target && event.target instanceof Element && !this.elementRef.nativeElement.contains(event.target) ) { this.onDragDrop.emit(event); } }); } ngOnDestroy(): void { this.destroy$.next(null); this.destroy$.complete(); } @HostListener('mousedown', ['$event']) onMouseDown(event: MouseEvent): void { this.mouseDown$.next(event); } @HostListener('document:mouseup', ['$event']) onMouseUp(event: MouseEvent): void { this.mouseUp$.next(event); } } ` Just like the previous directive example an event emitter is used so the user of the directive can associate custom functionality with it. RxJs is also utilized for the drag and drop detection. This directive uses the exhaustMap function to create an observable that emits both after a mouse down, and finally a mouse up is done. With that observable, we can subscribe to it and call the drag and drop callback so long as the element that’s dragged on isn’t the component itself. Note how the mouse down event is local to the component while the mouse up event is attached to the document. For mouse down, this is done since we only want the start of the dragging to be initiated from clicking the component itself. The mouse up must listen to the document since the dragging has to end on something that isn’t the component that we’re dragging. Just like the previous directive, we simply need to reference the attribute and register an event handler. ` Drag me over something! ` Conclusion In this article, we have learned how to write our own custom attribute directives and demonstrated a couple of practical examples of directives you might use or encounter in the real world. I hope you found this introduction to directives useful, and that it helps you with writing your own directives in the future! You can find the examples shown here in our blog demos repository if you want to use them yourself....

A Guide to (Typed) Reactive Forms in Angular - Part II (Building Dynamic Superforms) cover image

A Guide to (Typed) Reactive Forms in Angular - Part II (Building Dynamic Superforms)

In the first blog post of the series, we learned about Angular reactive forms and the data structures behind them. When developing real-world applications, however, you often need to leverage dynamic forms, as writing boilerplate for every form and its specific cases can be tedious and time-consuming. In certain situations, it may even be necessary to retrieve information from an API to construct the forms. In this post, we will go over a convenient abstraction we can create to build dynamic and adaptable forms without repeating boilerplate. The trick is to create a "view model" for our data and use a service to transform that data into a reactive form. I was first introduced to this approach by my friend and former teammate Thomas Duft, and I've been using it ever since. The approach outlined in the linked article worked great with untyped forms, but since now we can get our forms strictly typed, we'll want to upgrade it. And here is where it gets a bit tricky. Remember how I mentioned you shouldn't predeclare your form groups earlier? If you want to recursively create a form from a config, you just have to. And it's a dynamic form, so you cannot easily type it. To solve the issue, I devised a trick inspired by a "Super Form" suggested by Bobby Galli. Assuming we will have interfaces defined for our data, using this approach, we can create dynamic type-safe forms. First, we'll create types for our form config: `TypeScript // this will be our ViewModel for configuring a FormGroup export class FormSection | FormField | (FormSection | FormField)[]; } = any > { public title?: string; public fields: T; constructor(section: { title?: string; fields: T; }) { this.title = section.title; this.fields = section.fields; } } // Let's define some editor types we'll be using in the templates later export type FormEditor = | 'textInput' | 'passwordInput' | 'textarea' | 'checkbox' | 'select'; // And this will be a ViewModel for our FormControls export class FormField { public value: T; public editor: FormEditor; public validators: Validators; public label: string; public required: boolean; public options?: T[]; constructor(field: { value: T; editor: FormEditor; validators: Validators; label: string; required: boolean; options?: T[]; }) { this.value = field.value; this.editor = field.editor; this.validators = field.validators; this.label = field.label; this.required = field.required; this.options = field.options; } } ` And then we'll create some type mappings: `TypeScript // We will use this type mapping to properly declare our form group export type ControlsOf> = { [K in keyof T]: T[K] extends Array ? FormArray> : T[K] extends Record ? FormGroup> : FormControl; }; // We will use this type mapping to type our form config export type ConfigOf = { [K in keyof T]: T[K] extends (infer U)[] ? U extends Record ? FormSection>[] : FormField[] : T[K] extends Record ? FormSection> : FormField; }; ` And now we can use our types in a service that will take care of creating nested dynamic forms: `TypeScript import { Injectable } from '@angular/core'; import { AbstractControl, FormArray, FormControl, FormGroup, } from '@angular/forms'; import { ConfigOf, ControlsOf, FormField, FormSection } from './forms.model'; @Injectable({ providedIn: 'root', }) export class FormsService { public createFormGroup>( section: FormSection> ): FormGroup> { // we need to create an empty FormGroup first, so we can add FormControls recursively const group = new FormGroup({}); Object.keys(section.fields).forEach((key: any) => { const field = section.fields[key]; if (Array.isArray(field)) { group.addControl(key, this.createFormArray(field)); } else { if (field instanceof FormSection) { group.addControl(key, this.createFormGroup(field)); } else { group.addControl(key, new FormControl(field.value, field.validators)); } } }); // and we need to cast the group to the correct type before returning return group as unknown as FormGroup>; } public createFormArray>( fields: unknown[] ): FormArray> { const array: FormArray> = new FormArray( [] ) as unknown as FormArray>; fields.forEach((field) => { if (field instanceof FormSection) { array.push(this.createFormGroup(field)); } else { const { value, validators } = field as FormField; array.push(new FormControl(value, validators)); } }); return array as unknown as FormArray>; } } ` And that's it. Now we can use our FormService` to create forms. Let's say we have the following User model: `TypeScript export type User = { email: string; name: string; } ` We can create a form for this user from config in the following way: `TypeScript const userFormConfig = new FormSection>({ title: 'User Form', fields: { email: new FormField({ value: '', validators: [Validators.required, Validators.email], label: 'Email', editor: 'textInput', required: true, }), name: new FormField({ value: '', validators: [Validators.required], label: 'Name', editor: 'textInput', required: true, }) } }); const userForm = this.formsService.createFormGroup(userFormConfig); ` If we would check the type of userForm.value` now, we would see that it's correctly inferred as: `TypeScript Partial ` Outputting the Dynamic Forms To display the dynamic forms, we can write a simple component that takes the FormSection` or `FormField` as an `Input()` along with our `FormGroup` and displays the form recursively. We can use a setter to assign either field` or `section` property when the view model is passed into the component, so we can conveniently use them in our template. Our form component's TypeScript code will look something like this: `TypeScript import { Component, Input } from '@angular/core'; import { FormField, FormSection } from '../forms.model'; import { FormArray, FormGroup } from '@angular/forms'; @Component({ selector: 'app-form', templateUrl: './form.component.html', styleUrls: ['./form.component.scss'], }) export class FormComponent { private fieldConfig?: FormField; private sectionConfig?: FormSection; private arrayConfig?: (FormSection | FormField)[]; private sectionFieldsArray?: [string, FormField][]; @Input() public set config( config: | FormField | FormSection | (FormSection | FormField)[] ) { this.fieldConfig = config instanceof FormField ? config : undefined; this.arrayConfig = Array.isArray(config) ? config : undefined; this.sectionConfig = config instanceof FormSection ? config : undefined; this.sectionFieldsArray = Object.entries(this.sectionConfig?.fields || {}); } public get sectionFields(): [string, FormField][] { return this.sectionFieldsArray || []; } public get field(): FormField | undefined { return this.fieldConfig; } public get section(): FormSection | undefined { return this.sectionConfig; } public get array(): (FormSection | FormField)[] | undefined { return this.arrayConfig; } ngAfterViewInit() { console.log(this.arrayConfig); } @Input() public key!: string; @Input() public group!: FormGroup; public get sectionGroup(): FormGroup { return this.group.get(this.key) as FormGroup; } public get formArray(): FormArray { return this.group.get(this.key) as FormArray; } } ` And our template will reference a new form component for each section field in case we have passed in a FormSection` and it will have a switch case to display the correct control in case a `FormField` has been passed in: `HTML {{ field.label }} {{ section.title }} ` That way, we can display the whole form just by referencing one component, such as `HTML ` Check out an example on StackBlitz. In the next (and last) post of the series, we will learn about building custom Form Controls....

Being a CTO at Any Level: A Discussion with Kathy Keating, Co-Founder of CTO Levels cover image

Being a CTO at Any Level: A Discussion with Kathy Keating, Co-Founder of CTO Levels

In this episode of the engineering leadership series, Kathy Keating, co-founder of CTO Levels and CTO Advisor, shares her insights on the role of a CTO and the challenges they face. She begins by discussing her own journey as a technologist and her experience in technology leadership roles, including founding companies and having a recent exit. According to Kathy, the primary responsibility of a CTO is to deliver the technology that aligns with the company's business needs. However, she highlights a concerning statistic that 50% of CTOs have a tenure of less than two years, often due to a lack of understanding and mismatched expectations. She emphasizes the importance of building trust quickly in order to succeed in this role. One of the main challenges CTOs face is transitioning from being a technologist to a leader. Kathy stresses the significance of developing effective communication habits to bridge this gap. She suggests that CTOs create a playbook of best practices to enhance their communication skills and join communities of other CTOs to learn from their experiences. Matching the right CTO to the stage of a company is another crucial aspect discussed in the episode. Kathy explains that different stages of a company require different types of CTOs, and it is essential to find the right fit. To navigate these challenges, Kathy advises CTOs to build a support system of advisors and coaches who can provide guidance and help them overcome obstacles. Additionally, she encourages CTOs to be aware of their own preferences and strengths, as self-awareness can greatly contribute to their success. In conclusion, this podcast episode sheds light on the technical aspects of being a CTO and the challenges they face. Kathy Keating's insights provide valuable guidance for CTOs to build trust, develop effective communication habits, match their skills to the company's stage, and create a support system for their professional growth. By understanding these key technical aspects, CTOs can enhance their leadership skills and contribute to the success of their organizations....