<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Angular &#8211; Manuel Bogner&#039;s Blog</title>
	<atom:link href="https://blog.mbo.dev/archives/category/angular/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.mbo.dev</link>
	<description>Solutions to everyday IT problems</description>
	<lastBuildDate>Sun, 28 Nov 2021 20:42:13 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.mbo.dev/wp-content/uploads/2022/11/cropped-cropped-mbo-white_opt-32x32.png</url>
	<title>Angular &#8211; Manuel Bogner&#039;s Blog</title>
	<link>https://blog.mbo.dev</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Angular i18n</title>
		<link>https://blog.mbo.dev/archives/1687</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Sun, 28 Nov 2021 17:38:58 +0000</pubDate>
				<category><![CDATA[Angular]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1687</guid>

					<description><![CDATA[Angular has support for internationalisation with the @ngx-translate/core module. This and a needed loader can be installed via With this done you need to create a folder src/assets/i18n and place your language json file like en.json in it. Now the translation module and service needs to be configured. Open your app.module.ts file and add the [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Angular has support for internationalisation with the @<em>ngx-translate/core</em> module. This and a needed loader can be installed via</p>



<pre class="wp-block-code"><code>npm install @ngx-translate/core --save
<meta charset="utf-8">npm install @ngx-translate/http-loader --save</code></pre>



<p>With this done you need to create a folder <em>src/assets/i18</em>n and place your language json file like <em>en.json</em> in it.</p>



<p>Now the translation module and service needs to be configured. Open your <em>app.module.ts</em> file and add the follwing to your imports:</p>



<pre class="wp-block-code"><code>import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClientModule, HttpClient } from '@angular/common/http';</code></pre>



<pre class="wp-block-code"><code>TranslateModule.forRoot({
  defaultLanguage: DEFAULT_LANG,
  loader: {
    provide: TranslateLoader,
    useFactory: httpLoaderFactory,
    deps: &#91;HttpClient],
  },
})</code></pre>



<p>Where <meta charset="utf-8"><em>DEFAULT_LANG</em> is set as a const to &#8216;en&#8217;. <meta charset="utf-8"><em>httpLoaderFactory</em> is a function defined above:</p>



<pre class="wp-block-code"><code>export function httpLoaderFactory(http: HttpClient): TranslateHttpLoader {
  return new TranslateHttpLoader(http);
}</code></pre>



<p>To get it running I further added an app initialiser under providers:</p>



<pre class="wp-block-code"><code>import { APP_INITIALIZER, NgModule } from '@angular/core';</code></pre>



<pre class="wp-block-code"><code>{
  provide: APP_INITIALIZER,
  useFactory: initialise,
  deps: &#91;TranslateService],
  multi: true,
}</code></pre>



<p>Where <em>initialise</em> is a method again:</p>



<pre class="wp-block-code"><code>export function initialise(translate: TranslateService): any {
  return async () =&gt; {
    translate.setDefaultLang(DEFAULT_LANG);
    translate.use(DEFAULT_LANG);

    const translatePromise = new Promise&lt;void&gt;((resolve) =&gt; {
      translate.onLangChange.subscribe(() =&gt; {
        resolve();
      });
    });

    return Promise.all(&#91;translatePromise]);
  };
}</code></pre>



<h2 class="wp-block-heading">Usage</h2>



<p>Your translation files in assets/i18n are simple json structures that can be nested. Here an example:</p>



<pre class="wp-block-code"><code>{
  "MAIN": {
    "TITLE": "Hello",
    "TEXT": "world!"
  }
}</code></pre>



<p>You can access this in your html files by using</p>



<pre class="wp-block-code"><code>{{ 'MAIN.TITLE' | translate }}</code></pre>



<p>which would get the text <em>Hello</em>.</p>



<p>If you need to access translations  in your typescript files you can do so by injecting the TranslateService and then asynchronously fetch translations like this:</p>



<pre class="wp-block-preformatted">title: string;
constructor<em>(</em>private translate: TranslateService<em>) {</em>
  translate.get('MAIN.TITLE').subscribe((text: string) =&gt; (this.title = text));
}</pre>



<p>This will set the class variable <em>title</em> to the text from translation and you can access it like a normal variable in Angular.</p>



<h3 class="wp-block-heading">HTML</h3>



<p>If you need to include html in your translations you can use innerHtml like here:</p>



<pre class="wp-block-code"><code>&lt;p class="lead" &#91;innerHtml]="'MAIN.TEXT' | translate">&lt;/p></code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>First steps for a new angular application</title>
		<link>https://blog.mbo.dev/archives/1667</link>
		
		<dc:creator><![CDATA[Manuel Bogner]]></dc:creator>
		<pubDate>Sat, 27 Nov 2021 20:44:44 +0000</pubDate>
				<category><![CDATA[Angular]]></category>
		<guid isPermaLink="false">https://blog.coffeebeans.at/?p=1667</guid>

					<description><![CDATA[In the last weeks I dig deeper into Angular and how to structure such an application properly incl. the tools needed. So I want to share my first steps I take starting a new Angular app: Prettier What is Prettier? An opinionated code formatter Supports many languages Integrates with most editors Has few options Installation [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>In the last weeks I dig deeper into Angular and how to structure such an application properly incl. the tools needed. So I want to share my first steps I take starting a new Angular app:</p>



<h2 class="wp-block-heading">Prettier</h2>



<h3 class="wp-block-heading">What is Prettier?</h3>



<ul class="wp-block-list"><li>An opinionated code formatter</li><li>Supports many languages</li><li>Integrates with most editors</li><li>Has few options</li></ul>



<p></p>



<h3 class="wp-block-heading">Installation</h3>



<pre class="wp-block-code"><code>npm install prettier -D</code></pre>



<h3 class="wp-block-heading">Configuration</h3>



<p>Add <em>.prettierrc</em> file to your project:</p>



<pre class="wp-block-code"><code>{
  "tabWidth": 2,
  "useTabs": false,
  "singleQuote": true,
  "semi": true,
  "quoteProps": "as-needed",
  "trailingComma": "es5",
  "bracketSpacing": true,
  "bracketSameLine": true,
  "arrowParens": "always",
  "endOfLine": "lf",
  "printWidth": 100,
  "htmlWhitespaceSensitivity": "ignore"
}</code></pre>



<p>And also install a plugin to your IDE to use it.</p>



<p>I also add a script for prettier to my package.json:</p>



<pre class="wp-block-code"><code>"prettier": "prettier --write src/**/*.\\{ts,scss,html\\}"</code></pre>



<p>This allows me to run <strong><em>npm run prettier</em></strong> to format all my source files.</p>



<h2 class="wp-block-heading">ESLint</h2>



<p>ESLint statically analyzes your code to quickly find problems. ESLint is built into most text editors and you can run ESLint as part of your continuous integration pipeline.</p>



<p>Many problems ESLint finds can be automatically fixed. ESLint fixes are syntax-aware so you won&#8217;t experience errors introduced by traditional find-and-replace algorithms.</p>



<p>Preprocess code, use custom parsers, and write your own rules that work alongside ESLint&#8217;s built-in rules. You can customize ESLint to work exactly the way you need it for your project.</p>



<h3 class="wp-block-heading"><meta charset="utf-8">Installation</h3>



<pre class="wp-block-code"><code>ng add @angular-eslint/schematics</code></pre>



<h3 class="wp-block-heading"><meta charset="utf-8">Configuration</h3>



<p>Change <em>.eslintrc.json</em> to this:</p>



<pre class="wp-block-code"><code>{
  "root": true,
  "ignorePatterns": &#91;
    "projects/**/*"
  ],
  "overrides": &#91;
    {
      "files": &#91;
        "*.ts"
      ],
      "parser": "@typescript-eslint/parser",
      "parserOptions": {
        "project": &#91;
          "tsconfig.json",
        ],
        "createDefaultProgram": true
      },
      "plugins": &#91;
        "@typescript-eslint"
      ],
      "extends": &#91;
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates",
        "plugin:@typescript-eslint/recommended"
      ],
      "rules": {
        "@angular-eslint/component-selector": &#91;
          "error",
          {
            "prefix": "app",
            "style": "kebab-case",
            "type": "element"
          }
        ],
        "@angular-eslint/directive-selector": &#91;
          "error",
          {
            "prefix": "app",
            "style": "camelCase",
            "type": "attribute"
          }
        ],
        "@typescript-eslint/quotes": &#91;
          "error",
          "single",
          {
            "allowTemplateLiterals": true
          }
        ],
        "@angular-eslint/no-empty-lifecycle-method": "warn",
        "@angular-eslint/no-conflicting-lifecycle": "error",
        "@angular-eslint/no-host-metadata-property": "error",
        "@angular-eslint/no-input-rename": "error",
        "@angular-eslint/no-inputs-metadata-property": "error",
        "@angular-eslint/no-output-native": "error",
        "@angular-eslint/no-output-on-prefix": "error",
        "@angular-eslint/no-output-rename": "error",
        "@angular-eslint/no-outputs-metadata-property": "error",
        "@angular-eslint/use-lifecycle-interface": "error",
        "@angular-eslint/use-pipe-transform-interface": "error",
        "@typescript-eslint/adjacent-overload-signatures": "error",
        "@typescript-eslint/array-type": "off",
        "@typescript-eslint/ban-types": &#91;
          "error",
          {
            "types": {
              "Object": {
                "message": "Avoid using the `Object` type. Did you mean `object`?"
              },
              "Function": {
                "message": "Avoid using the `Function` type. Prefer a specific function type, like `() =&gt; void`."
              },
              "Boolean": {
                "message": "Avoid using the `Boolean` type. Did you mean `boolean`?"
              },
              "Number": {
                "message": "Avoid using the `Number` type. Did you mean `number`?"
              },
              "String": {
                "message": "Avoid using the `String` type. Did you mean `string`?"
              },
              "Symbol": {
                "message": "Avoid using the `Symbol` type. Did you mean `symbol`?"
              }
            }
          }
        ],
        "@typescript-eslint/consistent-type-assertions": "error",
        "@typescript-eslint/dot-notation": "error",
        "@typescript-eslint/indent": &#91;
          "error",
          2,
          {
            "FunctionDeclaration": {
              "parameters": "first"
            },
            "FunctionExpression": {
              "parameters": "first"
            },
            "SwitchCase": 1,
            "ignoredNodes": &#91;
              "TSTypeParameterInstantiation"
            ]
          }
        ],
        "@typescript-eslint/member-delimiter-style": &#91;
          "error",
          {
            "multiline": {
              "delimiter": "semi",
              "requireLast": true
            },
            "singleline": {
              "delimiter": "semi",
              "requireLast": false
            }
          }
        ],
        "@typescript-eslint/member-ordering": "error",
        "@typescript-eslint/no-empty-function": "off",
        "@typescript-eslint/no-empty-interface": "error",
        "@typescript-eslint/no-explicit-any": "off",
        "@typescript-eslint/no-inferrable-types": &#91;
          "error",
          {
            "ignoreParameters": true
          }
        ],
        "@typescript-eslint/no-misused-new": "error",
        "@typescript-eslint/no-namespace": "error",
        "@typescript-eslint/no-non-null-assertion": "error",
        "@typescript-eslint/no-parameter-properties": "off",
        "no-unused-vars": "off",
        "@typescript-eslint/no-unused-vars": &#91;
          "error"
        ],
        "no-shadow": "off",
        "@typescript-eslint/no-shadow": "error",
        "@typescript-eslint/no-unused-expressions": "error",
        "@typescript-eslint/no-use-before-define": "off",
        "@typescript-eslint/no-var-requires": "off",
        "@typescript-eslint/prefer-for-of": "error",
        "@typescript-eslint/prefer-function-type": "error",
        "@typescript-eslint/prefer-namespace-keyword": "error",
        "@typescript-eslint/semi": &#91;
          "error",
          "always"
        ],
        "@typescript-eslint/triple-slash-reference": &#91;
          "error",
          {
            "path": "always",
            "types": "prefer-import",
            "lib": "always"
          }
        ],
        "@typescript-eslint/type-annotation-spacing": "error",
        "@typescript-eslint/unified-signatures": "error",
        "@typescript-eslint/explicit-module-boundary-types": &#91;
          "error",
          {
            "allowArgumentsExplicitlyTypedAsAny": true
          }
        ],
        "prefer-const": &#91;
          "error",
          {
            "destructuring": "any",
            "ignoreReadBeforeAssign": true
          }
        ]
      }
    },
    {
      "files": &#91;
        "*.html"
      ],
      "parser": "@angular-eslint/template-parser",
      "extends": &#91;
        "plugin:@angular-eslint/template/recommended"
      ],
      "rules": {
        "@angular-eslint/template/banana-in-box": "error",
        "@angular-eslint/template/eqeqeq": "error",
        "@angular-eslint/template/no-negated-async": "error"
      }
    }
  ]
}</code></pre>



<p>ESLint replaces TSLint and has the benefit that it can handle more than just your .ts files.</p>



<h2 class="wp-block-heading">Bootstrap</h2>



<p>Quickly design and customize responsive mobile-first sites with Bootstrap, the world’s most popular front-end open source toolkit, featuring Sass variables and mixins, responsive grid system, extensive prebuilt components, and powerful JavaScript plugins.</p>



<p>Of course this step is optional and you can use whatever you like. But I&#8217;m still a fan of bootstrap and use it for my projects.</p>



<h3 class="wp-block-heading">Installation</h3>



<pre class="wp-block-code"><code>npm install bootstrap --save
npm install jquery --save
npm install popper.js --save</code></pre>



<p>To be able to use it you need to change your <em>angular.json</em> file section <em>…architect.build.options</em> from</p>



<pre class="wp-block-code"><code>{
  "styles": &#91;
    "src/styles.scss"
  ],
  "scripts": &#91;]
}</code></pre>



<p>to</p>



<pre class="wp-block-code"><code>{
  "styles": &#91;
    "node_modules/bootstrap/dist/css/bootstrap.min.css",
    "src/styles.scss"
  ],
  "scripts": &#91;
    "node_modules/jquery/dist/jquery.min.js",
    "node_modules/bootstrap/dist/js/bootstrap.min.js"
  ]
}</code></pre>



<p>There is also a section <meta charset="utf-8"><em>…architect.test.options</em> but I haven&#8217;t figured out yet if it&#8217;s necessary to place the new lines there as well or not.</p>



<h2 class="wp-block-heading">Font Awesome</h2>



<p>Get vector icons and social logos on your website with Font Awesome, the web&#8217;s most popular icon set and toolkit.</p>



<p>Another optional step but I like the icons and they&#8217;re free =)</p>



<h3 class="wp-block-heading">Installation</h3>



<pre class="wp-block-code"><code>npm install @fortawesome/fontawesome-svg-core
npm install @fortawesome/free-solid-svg-icons
npm install @fortawesome/angular-fontawesome</code></pre>



<p>With these in place open your <em>app.module.ts</em> and add the following import</p>



<pre class="wp-block-code"><code>import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';</code></pre>



<p>Also don&#8217;t forget to add <meta charset="utf-8">FontAwesomeModule into the import in the @NgModule in the same file.</p>



<h3 class="wp-block-heading">Usage</h3>



<p>With this in place you can import icons and then use them in your controllers and html. In your controller it looks like this</p>



<pre class="wp-block-code"><code>import { faCoffee } from "@fortawesome/free-solid-svg-icons";
...
export class HomeComponent {
  faCoffee = faCoffee;
}</code></pre>



<p>And in html you can reference it like this</p>



<pre class="wp-block-code"><code>&lt;fa-icon &#91;icon]="faCoffee"&gt;&lt;/fa-icon&gt;</code></pre>



<h2 class="wp-block-heading">Git Hooks</h2>



<p>Git per default isn&#8217;t running any hooks locally but there is an option to set a folder for such scripts. To enable this feature and set a folder you can use a script in your package.json:</p>



<pre class="wp-block-code"><code>"postinstall": "git config core.hooksPath ./.githooks"</code></pre>



<p>This command sets the local .githooks folder to be used for scripts when npm install is called. There is quite a list of possible scripts that can bee hooked. Here a list I found in the internet. If you need further details please just check the internet.</p>



<ul class="wp-block-list"><li>applypatch-msg</li><li>pre-applypatch</li><li>post-applypatch</li><li>pre-commit</li><li>prepare-commit-msg</li><li>commit-msg</li><li>post-commit</li><li>pre-rebase</li><li>post-checkout</li><li>post-merge</li><li>pre-receive</li><li>update</li><li>post-receive</li><li>post-update</li><li>pre-auto-gc</li><li>post-rewrite</li><li>pre-push</li></ul>



<p></p>



<h3 class="wp-block-heading">pre-commit</h3>



<p>It turned out to be quite useful to run prettier and lint before a commit. For this adding a file <em>.githooks/pre-commit</em> (I think it needs to be set executable) can be added with this simple content:</p>



<pre class="wp-block-code"><code>#!/bin/sh
npm run prettier --stages &amp;&amp; npm run lint || exit 1</code></pre>



<p>This will reformat all your code, add the changes to your commit and then lint your code. If one of the steps fails your commit fails as well. Sure your commit takes a while with this and for bigger projects this isn&#8217;t really an option. But for smaller projects it was quite handy.</p>



<h3 class="wp-block-heading">commit-msg</h3>



<p>Also enforcing a proper commit message structure before a commit can be made turned out to be a time safer compared to server side checks that would make you rewrite messages before you can push them. To easily validate your message you can use this script:</p>



<pre class="wp-block-code"><code>#!/usr/bin/env bash

# regex to validate in commit msg
commit_regex='(#&#91;0-9]+|merge) '
error_msg="Aborting commit. Your commit message needs to conform to $commit_regex"

if ! grep -iqE "$commit_regex" "$1"; then
    echo "$error_msg" &gt;&amp;2
    exit 1
fi</code></pre>



<p>This will enforce your commit message to include either an issue number like #42 or the text merge which is quite handy for gitlab.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
