Spring Boot Message Source Urls - Image

Spring Boot Message Source Urls

Spring Framework

Handling multiple environments, Locales and urls can be tricky. Here I’ll show you a trick on how to get things organized.

Spring boot comes with a predefined MessageSource that you can inject into any component.

For this example we are going to start by changing the default config to also include a urls resource bundle.

edit application.yml: spring.messages.basename=messages,urls

This will load messages both from

  • messages.properties
  • urls.properties

Production Profile

We now create a application-prod.yml file which will hold all our configurations for the prod profile.

We override basename so that it points at urls-prod.

application-prod.yml:

spring.messages.basename=messages,urls-prod

Url properties file layout

urls.properties:

site.url = http://www.myapp.dev
site.disclaimer = http://www.myapp.dev/disclaimer

portal.url = http://portal.myapp.dev
portal.account = http://portal.myapp.dev/account

urls_sv.properties:

portal.account = http://portal.myapp.dev/konto

And for the prod profile we change to the production urls

urls-prod.properties:

site.url = https://www.myapp.com
site.disclaimer = https://www.myapp.com/disclaimer

portal.url = https://portal.myapp.com
portal.account = https://portal.myapp.com/account

urls-prod_sv.properties:

portal.account = https://portal.myapp.com/konto

Typesafe access to the urls

Now that we have a extensible way of adding more urls and more languages we need a way to access the urls in a typesafe way. Since @ConfigurationProperties don’t support multiple languages The best way I’ve found is to create a custom service that uses the @Autowired MessageSource

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;

@Service
public class UrlProperties {

    private final MessageSource messageSource;

    private final SiteUrls site;
    private final PortalUrls portal;

    @Autowired
    public UrlProperties(MessageSource messageSource) {
        this.messageSource = messageSource;

        this.site = new SiteUrls();
        this.portal = new PortalUrls();
    }
    
  public SiteUrls getSite() {
    return site;
  }

    public PortalUrls getPortal() {
        return portal;
    }

  /**
   * Helper to get correct message from messageSource
   * There could of course be more advanced getters with parameters that pass them on to the getMessage
   * There could also be a version where you pass in the locale
   */
    private String get(String key) {
        return messageSource.getMessage(key, null, LocaleContextHolder.getLocaleContext().getLocale());
    }
    
    public class SiteUrls {
    public String getUrl() {
      return get("site.url");
    }

    public String getAdmin() {
      return get("site.disclaimer");
    }
  }

    public class PortalUrls {
        public String getUrl() {
            return get("portal.url");
        }

        public String getSignup() {
            return get("portal.account");
        }
    }

}

I hoped you liked the tutorial and that it gave you a good starting point for combining profiles and resource bundles.

Angular CLI and Bootstrap 4 - The Ultimate Setup - Image

Angular CLI and Bootstrap 4 - The Ultimate Setup

Angular

When starting out with angular-cli and bootstrap or material design, it’s not always straight forward how you should have your project setup to maximize flexibility and ease of component development.

In this tutorial I will show you my setup and why I’ve chosen this layout.

Lets start with the basics

ng new --skip-cli --style scss myapp
cd myapp
yarn

Note I’m assuming you installed the latest yarn and angular-cli

Next we’ll want to add bootstrap 4 and font-awesome

yarn add bootstrap@4.0.0-alpha.6
yarn add font-awesome

Setup bootstrap and app styles

By default angular-cli adds a styles.scss file in the src folder, but since we are going to have multiple scss files we’ll create a folder named src/styles/ We’ll also want to split our vendor styles into it’s own file. That way webpack won’t have to work as hard regenerating bootstrap’s scss files on every change.

In angular-cli.json change styles to be an array with two paths:

"styles": [
  "styles/vendor.scss",
  "styles/main.scss"
],

Lets start by creating src/styles/vendor.scss. Navigate to node_modules/bootstrap/scss/bootstrap.scss and copy the contents of that file to your vendor.scss.

We now need to change all the paths to point to node_modules. Sass has a shortcut for that:

@import "reboot";
// Becomes
@import "~bootstrap/scss/reboot";

At the top of the bootstrap imports we have variables and mixins. By having our own variables file imported at the beginning of the file, we will be able to override any bootstrap variable.

Add src/styles/_variables.scss and add it as an import at the top of vendor.scss

@import "variables";

To see all available variables navigate to node_modules/bootstrap/scss/_variables.scss

We are now set for bootstrap development.

Start with enabling a few of the modules, and as time goes by you might want to include more and more. But by doing it this way you won’t include more of bootstrap than you are using.

Font Awesome

The same process goes for including font-awesome in your build.

At the bottom of the vendor.scss file simply add scss $fa-font-path: "~font-awesome/fonts"; @import "~font-awesome/scss/font-awesome";

Webpack will automatically find the referenced font files and include them in the output.

The demo project is available here github.com/leon/blog-angular-cli-bootstrap-font-awesome

How to loop over multiple tr's with *ngFor - Image

How to loop over multiple tr's with *ngFor

Angular

I came across wanting to create a component in angular 2 that should display an edit section underneath a table row.

But because *ngFor only supports creating one element I hit a wall.

After some digging I found that by destructuring *ngFor into it’s standalone attributes I could use them on a <template> and be able to create what I wanted.


import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core';

interface Asset {
  id: number;
  name: string;
  price: number;
  change?: string;
}

@Component({
  selector: 'assets',
  template: `
    <table>
      <thead>
        <tr>
          <th>Id</th>
          <th>Name</th>
          <th>Price</th>
          <th></th>
        </tr>
      </thead>

      <tbody>
        <template ngFor let-asset [ngForOf]="assets" [ngForTrackBy]="assetTrackBy">
          <tr>
            <td></td>
            <td></td>
            <td></td>
            <td>
              <button type="button" (click)="showEdit(asset)" *ngIf="enableEdit && !asset.isEditing">Edit</button>
              <button type="button" (click)="cancelEdit(asset)" *ngIf="asset.isEditing">Cancel</button>
            </td>
          </tr>
          <tr *ngIf="asset.isEditing">
            <td colspan="4">
              <div class="bg-gray p-a-2">
                <textarea [(ngModel)]="asset.change"></textarea>
                <button type="button" (click)="change(asset)">Save</button>
              </div>
            </td>
          </tr>
        </template>
      </tbody>
    </table>
  `
})
export class AssetsComponent {

  @Input() assets: Asset[];
  @Input() enableEdit: boolean = false;

  @Output() save: EventEmitter<Asset> = new EventEmitter();

  showEdit(asset) {
    asset.isEditing = true;
  }

  cancelEdit(asset) {
    asset.isEditing = false;
    asset.change = undefined;
  }

  change(asset) {
    asset.isEditing = false;
    this.save.emit(asset);
  }

  assetsTrackBy(index: number, asset: Asset) {
    return asset.id;
  }
}