Using Angular with ZK"

From Documentation
m (correct highlight (via JWB))
 
(29 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
  {{Template:Smalltalk_Author|
 
  {{Template:Smalltalk_Author|
|author=Hawk Chen, Engineer, Potix Corporation
+
|author= Hawk Chen, Engineer, Potix Corporation
|date=  
+
|date= June 8, 2017
 
|version= ZK 8.0
 
|version= ZK 8.0
 
}}
 
}}
Line 7: Line 7:
 
= Overview =
 
= Overview =
 
[https://angular.io/ Angular]  
 
[https://angular.io/ Angular]  
<ref>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.</ref> is a well-known client-side MVW framework. In the previous article,[https://www.zkoss.org/wiki/Small_Talks/2016/May/Integrating_ZK_with_AngularJS 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.
+
<ref>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.</ref> is a well-known client-side MVW framework. In the previous article, [https://www.zkoss.org/wiki/Small_Talks/2016/May/Integrating_ZK_with_AngularJS 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 it is necessary to introduce the integration with Angular again.
  
 
In this article, I use the example in [https://angular.io/docs/ts/latest/tutorial/ Angular tutorial], hero editor, as the base and modify it to communicate with a Java ViewModel at the server-side with [http://books.zkoss.org/zk-mvvm-book/8.0/data_binding/client_binding_api.html client command binding].
 
In this article, I use the example in [https://angular.io/docs/ts/latest/tutorial/ Angular tutorial], hero editor, as the base and modify it to communicate with a Java ViewModel at the server-side with [http://books.zkoss.org/zk-mvvm-book/8.0/data_binding/client_binding_api.html client command binding].
 +
[[File:hero-editor.png | center]]
 +
 +
 +
<references/>
  
 
= Load Heroes from the Server =  
 
= Load Heroes from the Server =  
First, we need to rename <tt>index.html</tt> to <tt>index.'''zhtml'''</tt> to be processed by ZK and keep all existing tag elements unchanged in the file. Then add a ZK <tt><z:div></tt> for applying a ViewModel.  
+
First, we need to rename <code>index.html</code> to <code>index.'''zhtml'''</code> to be processed by ZK and keep all existing tag elements unchanged in the file. Then add a ZK <code><z:div></code> for applying a ViewModel.  
  
<source lang='xml' high='1,4'>
+
<source lang='xml' highlight='1,4'>
 
<html xmlns:z="zul" xmlns="native">
 
<html xmlns:z="zul" xmlns="native">
 
...
 
...
Line 25: Line 29:
  
 
</source>
 
</source>
* Line 1: We use [https://www.zkoss.org/wiki/ZUML_Reference/ZUML/Namespaces ZUML namespaces] to mix different compoennt sets in a file.
+
* Line 1: We use [https://www.zkoss.org/wiki/ZUML_Reference/ZUML/Namespaces ZUML namespaces] to mix different component sets in a file.
  
  
 
== Initialize Heroes ==
 
== Initialize Heroes ==
Since we suppose there are hero data at the server side, we need to create a <tt>Hero.java</tt>.
+
Since we suppose there are hero data at the server side, we need to create a <code>Hero.java</code>.
<source lang='java' high='8'>
+
<source lang='java' highlight='8'>
 
public class Hero {
 
public class Hero {
  
Line 51: Line 55:
 
public class HeroEditorVM {
 
public class HeroEditorVM {
  
private ArrayList<Hero> heroes = new ArrayList<Hero>();
+
private List<Hero> heroes = new ArrayList<Hero>();
private static Integer currentIndex = 10;
+
private HeroDao dao = new HeroDao();
 +
...
 +
}
 +
</source>
 +
 
 +
Create a ''Data Access Object'' to simulate a persistence layer.
 +
<source lang='java'>
 +
public class HeroDao {
 +
static Integer currentIndex = 10;
 +
static private ArrayList<Hero> heroes = new ArrayList<Hero>();
  
@Init
+
{
public void init() {
 
 
heroes.add(new Hero(nextId(), "Mr. Nice"));
 
heroes.add(new Hero(nextId(), "Mr. Nice"));
 
heroes.add(new Hero(nextId(), "Narco"));
 
heroes.add(new Hero(nextId(), "Narco"));
Line 67: Line 79:
 
heroes.add(new Hero(nextId(), "Tornado"));
 
heroes.add(new Hero(nextId(), "Tornado"));
 
}
 
}
...
+
 
 +
public List<Hero> queryAll(){
 +
return new LinkedList<Hero>(heroes);
 +
}
 +
 +
public Hero create(String name){
 +
...
 +
}
 +
 +
public void update(Hero hero){
 +
...
 +
}
 +
 +
public void remove(String id){
 +
...
 +
}
 +
 +
static Integer nextId(){
 +
return currentIndex++;
 +
}
 +
 
 
}
 
}
 
</source>
 
</source>
  
 
== Request to Get Heroes ==
 
== Request to Get Heroes ==
Originally, the <tt>HeroService.ts</tt> 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.
+
Originally, the <code>HeroService.ts</code> 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.
<source lang='js' high='6,11'>
+
 
 +
'''HeroService.ts'''
 +
<source lang='js' highlight='6,11'>
 
@Injectable()
 
@Injectable()
 
export class HeroService {
 
export class HeroService {
Line 89: Line 123:
 
}
 
}
 
</source>
 
</source>
* Line 6: We need to get a binder to trigger a command binding by <tt>binder.command('reload')</tt>.
+
* Line 6: We need to get a binder to trigger a command binding by <code>binder.command('reload')</code>.
  
To allow client command binding for "reload", we need to put <tt>@ToServerCommand({"reload"})</tt> on the class. In <tt>reload()</tt>, we don't need to do anything but just trigger a notification for the property "heroes". Combine [http://books.zkoss.org/zk-mvvm-book/8.0/syntax/notifycommand.html <tt>@NotifyCommand</tt>] and [http://books.zkoss.org/zk-mvvm-book/8.0/syntax/toclientcommand.html<tt>@ToClientCommand</tt>], ZK will invoke a JavaScript callback function when we notify change for "heroes" property.
+
To allow client command binding for "reload", we need to put <code>@ToServerCommand({"reload"})</code> on the class. In <code>reload()</code>, we call DAO to query all heroes and trigger a notification for the property "heroes". Combine [http://books.zkoss.org/zk-mvvm-book/8.0/syntax/notifycommand.html <code>@NotifyCommand</code>] and [http://books.zkoss.org/zk-mvvm-book/8.0/syntax/toclientcommand.html<code>@ToClientCommand</code>], ZK will invoke a JavaScript callback function when we notify change for "heroes" property.
  
<source lang='java' high='1,2,3,6'>
+
<source lang='java' highlight='1,2,3,9'>
 
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
 
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
 
@ToClientCommand({"updateHero"})
 
@ToClientCommand({"updateHero"})
 
@ToServerCommand({"reload"})
 
@ToServerCommand({"reload"})
 
public class HeroEditorVM {
 
public class HeroEditorVM {
...
+
 
 +
private List<Hero> heroes = new ArrayList<Hero>();
 +
private HeroDao dao = new HeroDao();
 +
 
 
@Command @NotifyChange("heroes")
 
@Command @NotifyChange("heroes")
public void reload(){
+
public void reload(@BindingParam("id")Integer id){
 +
if (id == null){
 +
heroes = dao.queryAll();
 +
}else{//query one
 +
...
 +
}
 
}
 
}
 
...
 
...
Line 109: Line 151:
  
  
Since we specify a to client command "updateHero" in the ViewModel (<tt>@ToClientCommand({"updateHero"})</tt>), we need to register a callback to receive heroes from the server and display 4 of them in the dashboard.
+
Since we specify a to client command "updateHero" in the ViewModel (<code>@ToClientCommand({"updateHero"})</code>), we need to register a callback to receive heroes from the server and display 4 of them in the dashboard.
<source lang='js' high='4, 9, 10'>
+
<source lang='js' highlight='4, 9, 10'>
 
...
 
...
 
export class DashboardComponent implements OnInit {
 
export class DashboardComponent implements OnInit {
Line 130: Line 172:
  
 
To simplify the implementation, we still get a list of heroes from the server and find the selected one by its ID.
 
To simplify the implementation, we still get a list of heroes from the server and find the selected one by its ID.
<source lang='js' high='9,13, 14'>
+
<source lang='js' highlight='9,13, 14'>
 
...
 
...
 
export class HeroDetailComponent implements OnInit {
 
export class HeroDetailComponent implements OnInit {
Line 139: Line 181:
  
 
   ngOnInit(): void {
 
   ngOnInit(): void {
     this.binder.after('getHero', hero =>{
+
     this.binder.after('updateHero', heroes =>{
       this.hero = hero;
+
       this.hero = heroes[0];
 
     });
 
     });
  
Line 150: Line 192:
 
</source>
 
</source>
 
* Line 9: register a callback to receive the selected heroe.
 
* 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. Please refer to [https://www.learnrxjs.io/operators/transformation/map.html here] for <tt> Observable.map()</tt>.
+
* Line 13-14: get the selected hero id from the route and get the hero. Please refer to [https://www.learnrxjs.io/operators/transformation/map.html here] for <code> Observable.map()</code>.
  
  
 
The service class calls the command method to get the selected Hero with ID as a parameter.
 
The service class calls the command method to get the selected Hero with ID as a parameter.
<source lang='js' high='5'>
+
<source lang='js' highlight='5'>
 
...
 
...
 
export class HeroService {
 
export class HeroService {
 
...
 
...
 
   getHero(id: number): void {
 
   getHero(id: number): void {
     this.binder.command('get', {'id':id});  
+
     this.binder.command('reload', {'id':id});  
 
   }
 
   }
 
</source>
 
</source>
  
  
Implement a method to get a hero and reponse the selected Hero back with <tt>@NotifyCommand</tt> and <tt>@ToClientCommand</tt>.
+
We still Implement getting one hero in <code>reload()</code> and rely on the same way to reponse the selected Hero back with <code>@NotifyCommand</code> and <code>@ToClientCommand</code>.
<source lang='java' high='3, 6,11,12'>
+
<source lang='java' highlight='1,2,7,11'>
@NotifyCommands({
+
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
@NotifyCommand(value="updateHero", onChange="_vm_.heroes"),
+
@ToClientCommand({"updateHero"})
@NotifyCommand(value="getHero", onChange="_vm_.selected")
+
@ToServerCommand({"reload", "delete", "add", "update"})
})
 
@ToClientCommand({"updateHero", "getHero"})
 
@ToServerCommand({"reload", "delete", "add", "update", "get"})
 
 
public class HeroEditorVM {
 
public class HeroEditorVM {
private Hero selected;
+
 
 
...
 
...
@Command  
+
@Command @NotifyChange("heroes")
@NotifyChange("selected")
+
public void reload(@BindingParam("id")Integer id){
public void get(@BindingParam("id")Integer id){
+
if (id == null){
for (Hero h: heroes){
+
heroes = dao.queryAll();
if (h.getId().equals(id)){
+
}else{//query one
selected = h;
+
Hero selected = null;
break;
+
for (Hero h: heroes){
 +
if (h.getId().equals(id)){
 +
selected = h;
 +
break;
 +
}
 
}
 
}
 +
heroes.clear();
 +
heroes.add(selected);
 
}
 
}
 
}
 
}
Line 192: Line 237:
  
 
Call client binding API to trigger a command with a parameter "name" in the ViewModel.
 
Call client binding API to trigger a command with a parameter "name" in the ViewModel.
<source lang='js'  high='5'>
+
<source lang='js'  highlight='5'>
 
...
 
...
 
export class HeroService {
 
export class HeroService {
Line 203: Line 248:
 
* Line 5: pass data with JSON format
 
* 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 <tt>@NotifyCommand</tt>, so that ZK will response back the new list of heroes.
+
Then implement a command method to add a hero in the ViewModel and remember to notify the change with <code>@NotifyCommand</code>, so that ZK will response back the new list of heroes.
<source lang='java' high='1,3 , 6, 7'>
+
<source lang='java' highlight='1,3 , 6, 7'>
 
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
 
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
 
@ToClientCommand({"updateHero"})
 
@ToClientCommand({"updateHero"})
Line 212: Line 257:
 
@Command @NotifyChange("heroes")
 
@Command @NotifyChange("heroes")
 
public void add(@BindingParam("name")String name){
 
public void add(@BindingParam("name")String name){
heroes.add(new Hero(nextId(), name));
+
heroes.add(dao.create(name));
 
}
 
}
 
...
 
...
 
}
 
}
 
</source>
 
</source>
* Line 3: add the command <tt>add</tt>
+
* Line 3: add the command <code>add</code>.
* Line 6: remember to notify the change, so that ZK will response back the new list of heroes because of line 1.
+
* 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)
+
* Line 7: ZK can convert JSON object to Java object for you (EE required).
  
  
 
Call hero service and receive a list of heroes.
 
Call hero service and receive a list of heroes.
<source lang='js' high='7,11'>
+
<source lang='js' highlight='7,11'>
 
...
 
...
 
export class HeroesComponent implements OnInit {
 
export class HeroesComponent implements OnInit {
Line 248: Line 293:
 
== List heroes at 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.
 
We can add a button to show the list of heroes on the server to confirm our client binding works.
<source lang='xml' high='5'>
+
<source lang='xml' highlight='5'>
 
     <z:div id="heroes"
 
     <z:div id="heroes"
 
     viewModel="@id('vm')@init('org.zkoss.zkangular.HeroEditorVM')">
 
     viewModel="@id('vm')@init('org.zkoss.zkangular.HeroEditorVM')">
Line 271: Line 316:
 
</source>
 
</source>
  
<source lang='js' high='4'>
+
<source lang='js' highlight='4'>
 
export class HeroService {
 
export class HeroService {
 
...
 
...
   delete(id: number): Promise<Hero> {
+
   delete(id: number): void {
 
     this.binder.command('delete', {'id':id});  
 
     this.binder.command('delete', {'id':id});  
 
   }
 
   }
Line 283: Line 328:
  
 
Implement "delete" command and notify the change to response a list of heroes back to the client side.
 
Implement "delete" command and notify the change to response a list of heroes back to the client side.
<source lang='java' high='3,9'>
+
<source lang='java' highlight='1,3,6'>
@NotifyCommands({
+
@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
@NotifyCommand(value="updateHero", onChange="_vm_.heroes"),
+
@ToClientCommand({"updateHero"})
@NotifyCommand(value="getHero", onChange="_vm_.selected")
+
@ToServerCommand({"reload", "delete", "add", "update"})
})
 
@ToClientCommand({"updateHero", "getHero"})
 
@ToServerCommand({"reload", "delete", "add", "update", "get"})
 
 
public class HeroEditorVM {
 
public class HeroEditorVM {
 
...
 
...
Line 298: Line 340:
 
</source>
 
</source>
  
Since we have already registered a callback function to update the list of heroes for <tt>updateHero</tt> command in <tt>ngOnInit()</tt>, client side will update the hero list after deletion.
+
Since we have already registered a callback function to update the list of heroes for <code>updateHero</code> command in <code>ngOnInit()</code>, client side will update the hero list after deletion.
  
 
= Update a Hero =
 
= Update a Hero =
  
Updating a hero is very similar to deleting one. Just call the service and receives a list of heroes. Please refer to the source for the details.
+
Updating a hero is very similar to deleting one. Just call the service and receive a list of heroes. Please refer to the source for the details.
  
= Download =  
+
= Download the Source=  
You can access the whole source at [https://github.com/zkoss-demo/zkangular2 github]. Please notice that I don't commit those 3rd-party packages (codes under <tt>webapp/node_nodeules</tt> into the repository. So you have to install with <tt>npm install</tt> first in order to run it.
+
You can access the complete source at [https://github.com/zkoss-demo/zkangular2 github].  
 +
 
 +
=Run the Project =
 +
# install dependencies
 +
#: run  <code>npm install</code> under the same folder as <code>package.json</code>.
 +
#: It will create <code>node_modules</code> and download javascript in it.
 +
#: ( I don't commit those 3rd-party packages (codes under <code>webapp/node_modules</code> into the repository. So you have to install javascript dependencies first before running the project. )
 +
# Compile typescript files
 +
#: install typescript compile <code>npm install -g typescript</code>
 +
#: compile with the command <code>tsc</code> and it will compiles all *.ts
 +
# copy installed <code>node_modules</code> to '''webapp/hero'''
 +
#: [[File:node_modules.png]]
 +
# run with maven command
 +
#: <code> mvn jetty:run</code>
 +
# visit http://localhost:8080/zkangular2/hero/index.zhtml
  
  
  
<references/>
 
  
 
{{Template:CommentedSmalltalk_Footer_new|
 
{{Template:CommentedSmalltalk_Footer_new|

Latest revision as of 04:32, 20 January 2022

Using Angular with ZK

Author
Hawk Chen, Engineer, Potix Corporation
Date
June 8, 2017
Version
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 it is necessary to introduce 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.

Hero-editor.png


  1. According to Angular naming guideline, we should use "Angular" for versions 2.0.0 and later.

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 a 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>


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 List<Hero> heroes = new ArrayList<Hero>();
	private HeroDao dao = new HeroDao();
...
}

Create a Data Access Object to simulate a persistence layer.

public class HeroDao {
	static Integer currentIndex = 10;
	static private ArrayList<Hero> heroes = new ArrayList<Hero>();

	{
		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"));
	}

	public List<Hero> queryAll(){
		return new LinkedList<Hero>(heroes);
	}
	
	public Hero create(String name){
		...
	}
	
	public void update(Hero hero){
		...
	}
	
	public void remove(String id){
		...
	}
	
	static Integer nextId(){
		return currentIndex++;
	}

}

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.

HeroService.ts

@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 call DAO to query all heroes and 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 {

	private List<Hero> heroes = new ArrayList<Hero>();
	private HeroDao dao = new HeroDao();

	@Command @NotifyChange("heroes")
	public void reload(@BindingParam("id")Integer id){
		if (id == null){
			heroes = dao.queryAll();
		}else{//query one
			...
		}
	}
...
}

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 heroes 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('updateHero', heroes =>{
      this.hero = heroes[0];
    });

    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. Please refer to here for Observable.map().


The service class calls the command method to get the selected Hero with ID as a parameter.

...
export class HeroService {
...
  getHero(id: number): void {
    this.binder.command('reload', {'id':id}); 
  }


We still Implement getting one hero in reload() and rely on the same way to reponse the selected Hero back with @NotifyCommand and @ToClientCommand.

@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
@ToClientCommand({"updateHero"})
@ToServerCommand({"reload", "delete", "add", "update"})
public class HeroEditorVM {

...
	@Command @NotifyChange("heroes")
	public void reload(@BindingParam("id")Integer id){
		if (id == null){
			heroes = dao.queryAll();
		}else{//query one
			Hero selected = null;
			for (Hero h: heroes){
				if (h.getId().equals(id)){
					selected = h;
					break;
				}
			}
			heroes.clear();
			heroes.add(selected);
		}
	}
...
}

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(dao.create(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 callback 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>

Delete a Hero

Call a the heroService that uses a client binding to delete the hero at the server side.

export class HeroesComponent implements OnInit {
...
  delete(hero: Hero): void {
    this.heroService
        .delete(hero.id);
  }
...
}
export class HeroService {
...
  delete(id: number): void {
    this.binder.command('delete', {'id':id}); 
  }
...
}


Implement "delete" command and notify the change to response a list of heroes back to the client side.

@NotifyCommand(value="updateHero", onChange="_vm_.heroes")
@ToClientCommand({"updateHero"})
@ToServerCommand({"reload", "delete", "add", "update"})
public class HeroEditorVM {
...
	@Command @NotifyChange("heroes")
	public void delete(@BindingParam("id")String id){
		..
	}

Since we have already registered a callback function to update the list of heroes for updateHero command in ngOnInit(), client side will update the hero list after deletion.

Update a Hero

Updating a hero is very similar to deleting one. Just call the service and receive a list of heroes. Please refer to the source for the details.

Download the Source

You can access the complete source at github.

Run the Project

  1. install dependencies
    run npm install under the same folder as package.json.
    It will create node_modules and download javascript in it.
    ( I don't commit those 3rd-party packages (codes under webapp/node_modules into the repository. So you have to install javascript dependencies first before running the project. )
  2. Compile typescript files
    install typescript compile npm install -g typescript
    compile with the command tsc and it will compiles all *.ts
  3. copy installed node_modules to webapp/hero
    Node modules.png
  4. run with maven command
    mvn jetty:run
  5. visit http://localhost:8080/zkangular2/hero/index.zhtml



Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.