Jerry's Blog

Recording what I learned everyday

View on GitHub


19 June 2019

Angular (6) -- Data Binding

by Jerry Zhang

Day 15:

package.json file

The dependencies used in production environment are listed in the dependencies attribute, whereas the ones used in development environment are listed in the devDependencies attribute.

Some commonly used npm commands:

Install dependencies:

npm i --save <dependency name>
or
npm i --save-dev <dependency name>

Create a new project:

ng new

Build in production environment:

ng build -prod

Start the server:

ng serve

Mock Rest API tool: json-server

npm install -g json-server

Create a json file first. Then import it with json server.

json-server ./mock/data.json

Passing data between components

Custom property binding

In the child component, we can define an input property in the typescript file.

@Input() element: {type: string, name: string, content: string};

This property element of course can be used in the current HTML file, but more important, by adding @Input, we can pass in an element from outside. The data source is stored in the parent component. In the parent HTML file, we can define a property called ‘element’ in the tag. In this way, we can pass in the element as an input to our child component.

serverElements = [{type: 'server', name: 'TestServer', content: 'A test'}];
<app-server-element
    *ngFor="let serverElement of serverElements"
    [element] = "serverElement"></app-server-element>

Then we can use this element in our child component.

<div class="panel panel-default">
  <div class="panel-heading"></div>
  <div class="panel-body">
    <p>
      <strong *ngIf="element.type === 'server'" style="color: red"></strong>
      <em *ngIf="element.type === 'blueprint'" style="color: blue"></em>
    </p>
  </div>
</div>

Binding custom event

An event will transport data in the other direction. It starts from a child component, for example, an input. This input value will be firstly saved in its own typescript file as an attribute. To pass out this value, we firstly need to define a EventEmitter an @Output on it.

  @Output() serverCreated = new EventEmitter<{serverName: string, serverContent: string}>();
  @Output() blueprintCreated = new EventEmitter<{serverName: string, serverContent: string}>();

These two objects can be used to emit an event when we click a button. For example, in the html file we define a button with an event click.

<button
    class="btn btn-primary"
    (click)="onAddServer()">Add Server</button>
<button
    class="btn btn-primary"
    (click)="onAddBlueprint()">Add Server Blueprint</button>

Then in the typescript file, we define these two methods.

onAddServer() {
    this.serverCreated.emit({serverName: this.newServerName, serverContent: this.newServerContent});
  }

  onAddBlueprint() {
    this.blueprintCreated.emit({serverName: this.newServerName, serverContent: this.newServerContent});
  }

In these two methods, we will use the two EventEmitter objects, serverCreated and blueprintCreated, that we defined in the first step, and emit an event with some data in JSON format.

Now, in the parent component, we can receive an event in the tag.

<app-cockpit
    (serverCreated)="onServerAdded($event)"
    (blueprintCreated)="onBlueprintAdded($event)"
  ></app-cockpit>

The final step is to define the corresponding methods in the parent component.

onServerAdded(serverData: {serverName: string, serverContent: string}) {
    this.serverElements.push({
      type: 'server',
      name: serverData.serverName,
      content: serverData.serverContent
    });
  }

  onBlueprintAdded(blueprintData: {serverName: string, serverContent: string}) {
    this.serverElements.push({
      type: 'blueprint',
      name: blueprintData.serverName,
      content: blueprintData.serverContent
    });
  }

CSS is different in Angular

Generally, css is applied in all the html documents. However, in Angular, css only get applied in its own component.

Every element in Angular has an additional attribute added by Angular at runtime. It simulates shadow DOM.

Overwrite the view encapsulation

In a component, if we write encapsulation: ViewEncapsulation.None, the css will apply globally.

Local references

Add a # in the input element.

 <input
      type="text"
      class="form-control"
      #serverNameInput>

Then we can refer it in the same html file.

<button
    class="btn btn-primary"
    (click)="onAddServer(serverNameInput)">Add Server</button>
<button
    class="btn btn-primary"
    (click)="onAddBlueprint(serverNameInput)">Add Server Blueprint</button>

Then we can use it in the ts file.

onAddServer(serverNameInput: HTMLInputElement) {
    this.serverCreated.emit({
      serverName: serverNameInput.value,
      serverContent: this.newServerContent
    });
  }

onAddBlueprint(serverNameInput: HTMLInputElement) {
    this.blueprintCreated.emit({
      serverName: serverNameInput.value,
      serverContent: this.newServerContent
    });
}

Getting access to the template and DOM with @ViewChild

We can also access the DOM with @ViewChild.

<input
  type="text"
  class="form-control"
  #serverContentInput>
@ViewChild('serverContentInput', { static: true }) serverContentInput: ElementRef;

onAddServer(serverNameInput: HTMLInputElement) {
    this.serverCreated.emit({
      serverName: serverNameInput.value,
      serverContent: this.serverContentInput.nativeElement.value
    });
  }

Putting content inside custom tags

By default, everything between and is lost. But we can change this.

We can put something inside custom components, but we need to put <ng-content></ng-content> in the same place as a hook.

Lifecycle

tags: Angular