Using Angular with ZK
Hawk Chen, Engineer, Potix Corporation
ZK 8.0
Overview
Angular [1] is a well-known client-side MVW framework. In the previous article,Integrating ZK with AngularJS, we have introduced how to integrate with AngularJS (1.x), but Angular changes a lot since 2.0. So we think that we also need to introduce more about the integration with Angular again.
In this article, I use the example in Angular tutorial, hero editor, as the base and modify it to communicate with a Java ViewModel at the server-side with client command binding.
Load Heroes from the Server
First, we need to rename index.html to index.zhtml to be processed by ZK and keep all existing tag elements unchanged in the file. Then add a ZK <z:div> for applying ViewModel.
<html xmlns:z="zul" xmlns="native">
...
<z:div id="heroes"
viewModel="@id('vm')@init('org.zkoss.zkangular.HeroEditorVM')">
<my-app>Loading...</my-app>
<br/><br/>
<z:button label="list all at the server" onClick="@command('show')"/>
</z:div>
- Line 1: We use ZUML namespaces to mix different compoennt sets in a file.
Initialize Heroes
Since we suppose there are hero data at the server side, we need to create a Hero.java.
public class Hero {
private int id;
private String name;
/**
* the no-argument constructor is required for converting a JSON into a Java object.
*/
public Hero(){
}
...
}
- Line 8: ZK can convert a JSON object into Java object for you, but the Java class must have a no-argument constructor.
After that, we create a list of Hero in a ViewModel.
public class HeroEditorVM {
private ArrayList<Hero> heroes = new ArrayList<Hero>();
private static Integer currentIndex = 10;
@Init
public void init() {
heroes.add(new Hero(nextId(), "Mr. Nice"));
heroes.add(new Hero(nextId(), "Narco"));
heroes.add(new Hero(nextId(), "Bombasto"));
heroes.add(new Hero(nextId(), "Celeritas"));
heroes.add(new Hero(nextId(), "Magneta"));
heroes.add(new Hero(nextId(), "RubberMan"));
heroes.add(new Hero(nextId(), "Dynama"));
heroes.add(new Hero(nextId(), "Dr IQ"));
heroes.add(new Hero(nextId(), "Magma"));
heroes.add(new Hero(nextId(), "Tornado"));
}
...
}
Request to Get Heroes
Originally, the HeroService.ts sends a request to an in-memory data service to get heroes. Now we change it to send a request to a ZK ViewModel with client command binding API.
@Injectable()
export class HeroService {
private headers = new Headers({'Content-Type': 'application/json'});
private heroesUrl = 'app/heroes'; // URL to web api
private binder = zkbind.$('$heroes');
constructor(private http: Http) { }
getHeroes(): void {
this.binder.command('reload');
}
...
}
- Line 6: We need to get a binder to trigger a command binding by binder.command('reload').
To allow client command binding for "reload", we need to put @ToServerCommand({"reload"}) on the class. In reload(), we don't need to do anything but just trigger a notification for the property "heroes". Combine @NotifyCommand and @ToClientCommand, ZK will invoke a JavaScript callback function when we notify change for "heroes" property.
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
@ToClientCommand({"updateHero"})
@ToServerCommand({"reload"})
public class HeroEditorVM {
...
@Command @NotifyChange("heroes")
public void reload(){
}
...
}
Show Heroes in the Dashboard
Since we specify a to client command "updateHero" in the ViewModel (@ToClientCommand({"updateHero"})), we need to register a callback to receive heroes from the server and display 4 of them in the dashboard.
...
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
binder = zkbind.$('$heroes');
...
ngOnInit(): void {
this.binder.after('updateHero', heroes => {
this.heroes = heroes.slice(1, 5);
});
this.heroService.getHeroes();
}
}
Show a Hero Detail
To simplify the implementation, we still get a list of hero from the server and find the selected one by its ID.
...
export class HeroDetailComponent implements OnInit {
hero: Hero;
private binder = zkbind.$('$heroes');;
...
ngOnInit(): void {
this.binder.after('getHero', hero =>{
this.hero = hero;
});
this.route.params.map((params: Params) => +params['id']).subscribe(id => {
this.heroService.getHero(id);
});
}
}
- Line 9: register a callback to receive the selected heroe.
- Line 13-14: get the selected hero id from the route and get the hero.
The service class calls the command method to get the selected Hero with ID as a parameter.
...
export class HeroService {
...
getHero(id: number): Promise<Hero> {
this.binder.command('get', {'id':id});
}
Implement a method to get a hero and reponse the selected Hero back with @NotifyCommand and @ToClientCommand.
@NotifyCommands({
@NotifyCommand(value="updateHero", onChange="_vm_.heroes"),
@NotifyCommand(value="getHero", onChange="_vm_.selected")
})
@ToClientCommand({"updateHero", "getHero"})
@ToServerCommand({"reload", "delete", "add", "update", "get"})
public class HeroEditorVM {
private Hero selected;
...
@Command
@NotifyChange("selected")
public void get(@BindingParam("id")Integer id){
for (Hero h: heroes){
if (h.getId().equals(id)){
selected = h;
break;
}
}
}
...
}
Add a Hero
Call client binding API to trigger a command with a parameter "name" in the ViewModel.
...
export class HeroService {
...
create(name: string):void {
this.binder.command('add', {'name':name});
}
...
- Line 5: pass data with JSON format
Then implement a command method to add a hero in the ViewModel and remember to notify the change with @NotifyCommand, so that ZK will response back the new list of heroes.
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
@ToClientCommand({"updateHero"})
@ToServerCommand({"reload", "add"})
public class HeroEditorVM {
@Command @NotifyChange("heroes")
public void add(@BindingParam("name")String name){
heroes.add(new Hero(nextId(), name));
}
...
}
- Line 3: add the command add
- Line 6: remember to notify the change, so that ZK will response back the new list of heroes because of line 1.
- Line 7: ZK can convert JSON object to Java object for you (EE required)
Call hero service and receive a list of heroes.
...
export class HeroesComponent implements OnInit {
...
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.heroService.create(name);
}
ngOnInit(): void {
this.binder.after('updateHero', this.setHeroes.bind(this));
this.getHeroes();
}
setHeroes(heroes){
this.heroes = heroes;
}
...
- Line 11: Register a callbacl function to receive a list of heroes from the server.
List heroes at the Server
We can add a button to show the list of heroes on the server to confirm our client binding works.
<z:div id="heroes"
viewModel="@id('vm')@init('org.zkoss.zkangular.HeroEditorVM')">
<my-app>Loading...</my-app>
<br/><br/>
<z:button label="list heroes at the server" onClick="@command('show')"/>
</z:div>
Update a Hero
Delete a Hero
Download
- ↑ According to [http://angularjs.blogspot.tw/2016/12/ok-let-me-explain-its-going-to-be.html Angular naming guideline, we should use "Angular" for versions 2.0.0 and later.
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |