Total Pageviews

2017/03/10

[Agnular2] Notifying parent Components that something has happened via events

Scenario

If we would like to tell the parent Component that the child component has added or deleted subtask. How to implement in Angualr2 ?


How-to
To create a custom event, we can use the new @Output decorator. So let's start by creating a new instance of an Event Emitter and decorate the property with the @Output decorator.

Then we've got an event emitter in place, let's call its emit method, callParent(), to raise the event and pass to parent component. 

The child component's code snippet looks like:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component({
  selector: 'sub-task',
  directives: [MODAL_DIRECTIVES, SELECT_DIRECTIVES, NgClass],
  providers: [SubtaskService, UserService, IssueService, ProjectService],
  templateUrl: 'sub.task.html'
})
export class SubTaskComponent implements OnInit {

  @Input() issue: Issue;
  @Input() watchers: Watcher[];

  @Output() refreshIssueEvent = new EventEmitter();
  
  callParent() {
    this.refreshIssueEvent.emit(this.issue.id);
  }
  
  //...
}  


The parent component receives that event and its payload. We use event binding to bind to this notify event and call a method.

Our final step is to provide the refreshIssue method to execute when the notify event occurs. 
1
2
3
<div class="container" *ngIf="issue.id">
  <sub-task [issue]="issue" [watchers]="issue.watchers" (refreshIssueEvent)="refreshIssue($event)"></sub-task>
</div>

We have to pass the $event to parent component's handler because that variable holds the event payload.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Component({
  selector: 'issue-detail',
  templateUrl: 'issue.detail.html',
  providers: [IssueService]
})
export class IssueDetailComponent implements OnInit, OnDestroy {
  refreshIssue(issueId: number){
    this.issueService.getIssue(issueId.toString()).subscribe(
      data => {
        this.issue = data;
      }
    );
  }
  //...
}

2017/03/09

[Angular2] Pass data from parent to child with input binding

Scenario


If we had two html pages which has parent and child relationship. If we would like to pass parameters from parent to child, how to implement in Agnular2?

How-to
Step 1. Define the input parameter names in child component which typically adorned with @Input decorations., the code snippet is as bellows:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Component({
  selector: 'sub-task',
  directives: [MODAL_DIRECTIVES, SELECT_DIRECTIVES, NgClass],
  providers: [SubtaskService, UserService, IssueService, ProjectService],
  templateUrl: 'sub.task.html'
})
export class SubTaskComponent implements OnInit {

  @Input() issue: Issue;
  @Input() watchers: Watcher[];
  
  // ...
}

Step 2. The Parent page, issue.detail.html, is binding its master string property to the child's property. The code snippet is as follows:
1
2
3
<div class="container" *ngIf="issue.id">
  <sub-task [issue]="issue" [watchers]="issue.watchers"></sub-task>
</div>



2017/03/08

[Design Pattern] Command Pattern

在 97 Things Every Programmer Should Know 一書中提到:
雖然在一般遇到的案例裡,使用 if - then - else 比多型更實用些,但絕大多數用 polymorphism 的寫作風格會讓程式碼更精簡、更易讀,且更少脆弱的程式碼。所以在我們的程式碼裡,要是錯過使用 polymorphism 的機會,便會逐漸增加  if - then - else 敘述句的數量。試試 command  pattern 吧

假設我有一個機器人物件,他會執行前進、後退、左轉與右轉,這四種指令

package albert.practice.designpattern.command;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@AllArgsConstructor
@Data
@Slf4j
public class Robot {

    private String robotName;

    public void goAhead() {
        log.debug(robotName + " 前進 !");
    }

    public void turnLeft() {
        log.debug(robotName + " 左轉 !");
    }

    public void turnRight() {
        log.debug(robotName + " 右轉 !");
    }

    public void turnBack() {
        log.debug(robotName + " 後退 !");
    }
}


另外建立一個 enumeration 來判斷,此次呼叫要執行哪一個指令
package albert.practice.designpattern.command;

public enum CommandEnum {
    GO_HEAD, TURN_BACK, TURN_RIGHT, TURN_LEFT;
}


建立機器人物件與執行指令如下:
package albert.practice.designpattern.command;

public class RobotTestClient {

    public static void main(String[] args) {
        Robot robot = new Robot("瓦力");

        RobotTestClient client = new RobotTestClient();
        client.executeRobot(robot, CommandEnum.GO_HEAD);
        client.executeRobot(robot, CommandEnum.TURN_LEFT);
        client.executeRobot(robot, CommandEnum.TURN_RIGHT);        
    }

    public void executeRobot(Robot robot, CommandEnum command) {
        if (CommandEnum.GO_HEAD == command) {
            robot.goAhead();
        } else if (CommandEnum.TURN_BACK == command) {
            robot.turnBack();
        } else if (CommandEnum.TURN_LEFT == command) {
            robot.turnLeft();
        } else if (CommandEnum.TURN_RIGHT == command) {
            robot.turnRight();
        }
    }

}



在上述的程式碼就會發現,如果指令越來越多,就會有越來越多的 if-else 判斷式,此時就可以套用 command pattern

首先,先建立 Command interface
package albert.practice.designpattern.command;

public interface Command {
    void execute();
}



然後,分別建立四個指令的 classes,並實作 Command interface
前進指令:
package albert.practice.designpattern.command;

public class GoAheadCommand implements Command {

    private Robot robot;

    public GoAheadCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.goAhead();
    }

}


後退指令:
package albert.practice.designpattern.command;

public class TurnBackCommand implements Command {
    private Robot robot;

    public TurnBackCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.turnBack();
    }

}



向左轉指令:

package albert.practice.designpattern.command;

public class TurnLeftCommand implements Command {
    private Robot robot;

    public TurnLeftCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.turnLeft();
    }

}



向右轉指令:
package albert.practice.designpattern.command;

public class TurnRightCommand implements Command {
    private Robot robot;

    public TurnRightCommand(Robot robot) {
        super();
        this.robot = robot;
    }

    @Override
    public void execute() {
        this.robot.turnRight();
    }

}



建立機器人物件與操作指令,程式修改如下:

package albert.practice.designpattern.command;

public class RobotTestClient {

    public static void main(String[] args) {
        Robot robot = new Robot("瓦力");
        new GoAheadCommand(robot).execute();
        new TurnLeftCommand(robot).execute();
        new TurnRightCommand(robot).execute();
    }

}



2017/03/07

[Redmine] How to get mail notification value in user page

Problem
I am trying to get admin value from the redmine user object. 
But I cannot get admin value, no matter using Java API or Rest API.
It can only retrieve very limited information:



How-To
We can make good use of jsoup to parse html page and get value from the specific html element.

Firstly, checking the drop down list element in html. 


Secondly, using jsoup to parse HTML
1
2
3
4
5
6
7
8
9
    String url = "http://192.168.0.1/users/" + userId + "/edit";
    Document document = Jsoup.connect(url).get();
    Elements mailNotificationElements = document.select("[name=user[mail_notification]]").select("option");
    for (Element element : mailNotificationElements) {
        if ("selected".equals(element.attr("selected"))) {
            log.debug("the mail_notification value = " + element.val());
            break;
        }
    }


Reference
[1] https://jsoup.org/



2017/03/06

[Redmine] How to get admin value in user page

Problem
I am trying to get admin value from the redmine user object. 
But I cannot get admin value, no matter using Java API or Rest API.
It can only retrieve very limited information:



How-To
We can make good use of jsoup to parse html page and get value from the specific html element.

Firstly, checking the checkbox element in html. 
We can find out the element name and the checked checkbox will add checked="checked" attribute.

If the checkbox does not be checked, it will not have checked="checked" attribute.


Secondly, using jsoup to parse HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    String url = "http://192.168.0.1/users/" + userId + "/edit";
    // Connect to redmine url and get HTML document
    Document document = Jsoup.connect(url).get();
    
    // Find elements that match [name=user[admin]]
    Elements adminElements = document.select("[name=user[admin]]");
    for (Element element : adminElements) {
       // if the element has checked="checked" attribute, it means this user is administrator, 
       // or this user is a normal user
       if ("checked".equals(element.attr("checked"))) {
           log.debug("this user is admin");
           break;
       }
    }


Reference
[1] https://jsoup.org/