blob: 8bab7f22f54012de0e98c1127a581604b64327b5 [file] [log] [blame]
<!doctype html>
<html class="no-js" lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>NetBeans Platform Plugins with DukeScript</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="NetBeans Platform Plugins with DukeScript - Apache NetBeans">
<meta name="author" content="Apache NetBeans">
<meta name="description" content="NetBeans Platform Plugins with DukeScript - Apache NetBeans">
<meta name="keywords" content="Apache NetBeans Platform, Platform Tutorials, NetBeans Platform Plugins with DukeScript">
<meta name="generator" content="Apache NetBeans">
<link rel="stylesheet" href="../../../../_/css/font-awesome.min.css">
<link rel="alternate" type="application/atom+xml" title="Apache NetBeans Blog" href="https://netbeans.apache.org/blogs/atom" />
<link rel="stylesheet" href="../../../../_/css/highlightjs/default.min.css">
<link rel="stylesheet" href="../../../../_/css/netbeans.css">
<link rel="apple-touch-icon" sizes="180x180" href="../../../../_/images/fav/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="../../../../_/images/fav/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="../../../../_/images/fav/favicon-16x16.png">
<link rel="manifest" href="../../../../_/images/fav/site.webmanifest">
<link rel="mask-icon" href="../../../../_/images/fav/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#ffc40d">
<meta name="theme-color" content="#ffffff">
<link href="../../../../_/css/font-open-sans.css" rel="stylesheet">
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
</head>
<body>
<div class="title-bar" data-responsive-toggle="responsive-menu" data-hide-for="medium">
<button type="button" data-toggle="responsive-menu"><i style='font-size: 32px; color: #fff; padding: 8px' class='fa fa-bars'></i></button>
<div class="title-bar-title">Apache NetBeans</div>
</div>
<div class="top-bar" id="responsive-menu">
<div class='top-bar-left'>
<a class='title' href="../../../../index.html"><img src='../../../../_/images/apache-netbeans.svg' style='padding: 8px; height: 48px;'> Apache NetBeans</a>
</div>
<div class="top-bar-right">
<ul class="vertical medium-horizontal menu" data-responsive-menu="drilldown medium-dropdown">
<li> <input id="search-input" type="text" placeholder="Search the docs"> </li>
<li> <a href="../../../../front/main/community">Community</a> </li>
<li> <a href="../../../../front/main/participate">Participate</a> </li>
<li> <a href="../../../../front/main/blogs">Blog</a></li>
<li> <a href="../../../../front/main/help">Get Help</a> </li>
<li> <a href="https://plugins.netbeans.apache.org/">Plugins</a> </li>
<li> <a href="../../../../front/main/download">Download</a> </li>
</ul>
</div>
</div>
<!-- src/templates/news -->
<section class="hero news alternate">
<div class='grid-container'>
<div class='cell'>
<div class="annotation">Latest release</div>
<h1>Apache NetBeans 28</h1>
<p><a class="button success" href="../../../../front/main/download/nb28">Download</a></p>
</div>
</div>
</section>
<div class='grid-container main-content tutorial'>
<h1 class="sect0">NetBeans Platform Plugins with DukeScript</h1>
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tbody><tr>
<td class="icon"><i class="fa icon-note" title="Note"></i></td>
<td class="content">This tutorial needs a review.
You can <a href="https://github.com/apache/netbeans-antora-tutorials/edit/main/modules/ROOT/pages/tutorials/nbm-dukescript.adoc" title="Edit this tutorial in github">edit it in GitHub </a>
following these <a href="../../../../tutorial/main/kb/docs/contributing">contribution guidelines.</a></td>
</tr></tbody>
</table>
</div>
</div>
<div id="toc" class="toc">
<div id="toctitle"></div>
<ul class="sectlevel1">
<li><a href="#_the_github_rest_api">The Github REST API</a></li>
<li><a href="#_installing_the_plugin">Installing the Plugin</a></li>
<li><a href="#_create_the_projects">Create the Project(s)</a></li>
<li><a href="#_coding_the_module">Coding the Module</a>
<ul class="sectlevel2">
<li><a href="#_code_the_view_logic">Code the View Logic</a></li>
<li><a href="#_create_the_view">Create the View</a></li>
</ul>
</li>
<li><a href="#_see_also">See Also</a></li>
</ul>
</div>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Welcome to a new era for NetBeans plugin development!</p>
</div>
<div class="paragraph">
<p>This tutorial shows you how you can use the <a href="https://bits.netbeans.org/html+java/1.2.3/index.html">html4java API</a> (aka DukeScript) to create NetBeans Plugins. This API allows you to define your view using HTML 5, and code the view logic in Java. After completing this tutorial, you&#8217;ll be able to create Plugins for the NetBeans IDE, or for your NetBeans Platform Application with this new Technology.</p>
</div>
<div class="paragraph">
<p>After you finish this tutorial, you can move on to the <a href="https://dukescript.com/documentation.html">DukeScript Library</a>. This page provides comprehensive documentation of the DukeScript APIs and many helpful tutorials.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This document uses NetBeans IDE 8.1 and NetBeans Platform 8.1.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>For troubleshooting purposes, the complete project is available for download on Github: <a href="https://github.com/dukescript/github-nb-plugin">Tutorial source code</a>.</p>
</div>
<div class="paragraph">
<p>The Window you create in this tutorial will look as follows:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../../_images/tutorials/dukescript_81_repos-plugin.png" alt="dukescript 81 repos plugin">
</div>
</div>
<div class="paragraph">
<p>When the user enters a Github username in the input field, the window displays a list of projects of this user.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_github_rest_api"><a class="anchor" href="#_the_github_rest_api"></a>The Github REST API</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We&#8217;ll use the <a href="https://developer.github.com/v3/">Github REST API</a> to provide the data for our tutorial. The following command lists all repositories for a github account (actually the Jersey JAX-RS developers account):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-shell hljs" data-lang="shell">$ curl https://api.github.com/users/jersey/repos</code></pre>
</div>
</div>
<div class="paragraph">
<p>The ouput of this query has the following format:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">[
{
"id": 6109440,
"name": "hol-sse-websocket",
"full_name": "jersey/hol-sse-websocket",
"owner": {
"login": "jersey",
"id": 399710,
"avatar_url": "https://avatars.githubusercontent.com/u/399710?v=3",
"gravatar_id": "",
"url": "https://api.github.com/users/jersey",
"html_url": "https://github.com/jersey",
"followers_url": "https://api.github.com/users/jersey/followers",
"following_url": "https://api.github.com/users/jersey/following{/other_user}",
"gists_url": "https://api.github.com/users/jersey/gists{/gist_id}",
"starred_url": "https://api.github.com/users/jersey/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/jersey/subscriptions",
"organizations_url": "https://api.github.com/users/jersey/orgs",
"repos_url": "https://api.github.com/users/jersey/repos",
"events_url": "https://api.github.com/users/jersey/events{/privacy}",
"received_events_url": "https://api.github.com/users/jersey/received_events",
"type": "Organization",
"site_admin": false
},
"private": false,
"html_url": "https://github.com/jersey/hol-sse-websocket",
"description": "Hands-on-lab on using server-sent events and web socket with Jersey and Tyrus.",
"fork": false,
"url": "https://api.github.com/repos/jersey/hol-sse-websocket",
"forks_url": "https://api.github.com/repos/jersey/hol-sse-websocket/forks",
"keys_url": "https://api.github.com/repos/jersey/hol-sse-websocket/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/jersey/hol-sse-websocket/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/jersey/hol-sse-websocket/teams",
"hooks_url": "https://api.github.com/repos/jersey/hol-sse-websocket/hooks",
"issue_events_url": "https://api.github.com/repos/jersey/hol-sse-websocket/issues/events{/number}",
"events_url": "https://api.github.com/repos/jersey/hol-sse-websocket/events",
"assignees_url": "https://api.github.com/repos/jersey/hol-sse-websocket/assignees{/user}",
"branches_url": "https://api.github.com/repos/jersey/hol-sse-websocket/branches{/branch}",
"tags_url": "https://api.github.com/repos/jersey/hol-sse-websocket/tags",
"blobs_url": "https://api.github.com/repos/jersey/hol-sse-websocket/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/jersey/hol-sse-websocket/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/jersey/hol-sse-websocket/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/jersey/hol-sse-websocket/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/jersey/hol-sse-websocket/statuses/{sha}",
"languages_url": "https://api.github.com/repos/jersey/hol-sse-websocket/languages",
"stargazers_url": "https://api.github.com/repos/jersey/hol-sse-websocket/stargazers",
"contributors_url": "https://api.github.com/repos/jersey/hol-sse-websocket/contributors",
"subscribers_url": "https://api.github.com/repos/jersey/hol-sse-websocket/subscribers",
"subscription_url": "https://api.github.com/repos/jersey/hol-sse-websocket/subscription",
"commits_url": "https://api.github.com/repos/jersey/hol-sse-websocket/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/jersey/hol-sse-websocket/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/jersey/hol-sse-websocket/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/jersey/hol-sse-websocket/issues/comments/{number}",
"contents_url": "https://api.github.com/repos/jersey/hol-sse-websocket/contents/{+path}",
"compare_url": "https://api.github.com/repos/jersey/hol-sse-websocket/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/jersey/hol-sse-websocket/merges",
"archive_url": "https://api.github.com/repos/jersey/hol-sse-websocket/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/jersey/hol-sse-websocket/downloads",
"issues_url": "https://api.github.com/repos/jersey/hol-sse-websocket/issues{/number}",
"pulls_url": "https://api.github.com/repos/jersey/hol-sse-websocket/pulls{/number}",
"milestones_url": "https://api.github.com/repos/jersey/hol-sse-websocket/milestones{/number}",
"notifications_url": "https://api.github.com/repos/jersey/hol-sse-websocket/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/jersey/hol-sse-websocket/labels{/name}",
"releases_url": "https://api.github.com/repos/jersey/hol-sse-websocket/releases{/id}",
"created_at": "2012-10-07T04:44:32Z",
"updated_at": "2014-06-29T22:29:42Z",
"pushed_at": "2013-05-29T16:56:03Z",
"git_url": "git://github.com/jersey/hol-sse-websocket.git",
"ssh_url": "git@github.com:jersey/hol-sse-websocket.git",
"clone_url": "https://github.com/jersey/hol-sse-websocket.git",
"svn_url": "https://github.com/jersey/hol-sse-websocket",
"homepage": null,
"size": 7750,
"stargazers_count": 11,
"watchers_count": 11,
"language": "Java",
"has_issues": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"forks_count": 5,
"mirror_url": null,
"open_issues_count": 1,
"forks": 5,
"open_issues": 1,
"watchers": 11,
"default_branch": "master"
},
{
"etc." : "etc."
}
]</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_installing_the_plugin"><a class="anchor" href="#_installing_the_plugin"></a>Installing the Plugin</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Before we start coding we&#8217;ll install a Plugin that helps us develop applications with DukeScript.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="1">
<li>
<p>Go to Tools/Plugins, refresh the catalog, select available plugins tab and install <strong>DukeScript Project Wizard</strong>.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p><span class="image"><img src="../../_images/tutorials/dukescript_install.png" alt="dukescript install"></span> This will require a restart of the IDE.</p>
</div>
<div class="paragraph">
<p>After that, you&#8217;ll have a handy wizard that helps you create a new project. We&#8217;ll use it to create our demo project in the next section.
Under the hood this wizard uses Maven to create a project from an Archetype. Alternatively you can also use the Maven Archetypes directly from the command line:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">mvn archetype:generate
-DarchetypeGroupId=com.dukescript.archetype
-DarchetypeArtifactId=knockout4j-archetype
-DarchetypeVersion=0.11</code></pre>
</div>
</div>
<div class="paragraph">
<p>The archetype can generate subprojects for each of the supported platforms. Currently we support iOS, Desktop (via JavaFX), Android, NetBeans plugin, and Browser (via bck2brwsr). The JavaFX-based project will always be generated, as this is integrated with the NetBeans visual debugger and the other debugging functions. The other subprojects are only generated on demand using these properties:
Run in Browser:
<code>-Dwebpath=client-web</code>
Create NetBeans Module:
<code>-Dnetbeanspath=client-netbeans</code>
Create iOS project:
<code>-Diospath=client-ios </code>
Create Android project:
<code>-Dandroidpath=client-android</code></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_create_the_projects"><a class="anchor" href="#_create_the_projects"></a>Create the Project(s)</h2>
<div class="sectionbody">
<div class="olist arabic">
<ol class="arabic" start="1">
<li>
<p>Now create new project (<strong>File | New Project&#8230;&#8203;</strong>). In the New Project Wizard switch to category <strong>DukeScript</strong>. Choose the template <strong>DukeScript Application</strong>:</p>
</li>
</ol>
</div>
<div class="paragraph">
<p><span class="image"><img src="../../_images/tutorials/dukescript_dukescript1.jpg" alt="dukescript dukescript1"></span> Click Next.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="2">
<li>
<p>In Step 2 you need to specify the location where to create your project and the Maven coordinates.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p><span class="image"><img src="../../_images/tutorials/dukescript_dukescript2.png" alt="dukescript dukescript2"></span> Click Next.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="3">
<li>
<p>The next wizard steps ask to what platforms you would like to deploy. The options are Android, iOS, Browser and NetBeans Plugin. In addition to that a Desktop Client will automatically created for you. This is the one that is used for testing and debugging. Select "Run as NetBeans Plugin" in this step:</p>
</li>
</ol>
</div>
<div class="paragraph">
<p><span class="image"><img src="../../_images/tutorials/dukescript_wizard_4.png" alt="dukescript wizard 4"></span></p>
</div>
<div class="olist arabic">
<ol class="arabic" start="4">
<li>
<p>In Step 4 you can choose between the available project templates. Let&#8217;s go for the simplest one <strong>Knockout 4 Java Maven Archetype</strong>. In this step you can also choose to install some sample code. But for this tutorial we&#8217;ll start from scratch:</p>
</li>
</ol>
</div>
<div class="paragraph">
<p><span class="image"><img src="../../_images/tutorials/dukescript_wizard_5.png" alt="dukescript wizard 5"></span> Click Finish.</p>
</div>
<div class="paragraph">
<p>The Maven archetype will create a parent project, that contains several subprojects. There&#8217;s always the "General Client Code". This is the project that contains the actual source Java code. Use this to develop, test and debug your project. There&#8217;s also a separate project for JavaScript Libraries. The code in here makes calls to JavaScript and back, which is sometimes necessary to achieve what you want.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../../_images/tutorials/dukescript_subprojects.png" alt="dukescript subprojects">
</div>
</div>
<div class="paragraph">
<p>A "project" is a NetBeans IDE compilation/deployment unit. It contains a Maven POM file, which on disk is named "pom.xml". This is a configuration file that contains all information required for compiling and running the project. The project contains all of your sources. The project opens in the IDE. You can view its logical structure in the Projects window (Ctrl+1) and its file structure in the Files window (Ctrl+2).</p>
</div>
<div class="paragraph">
<p>For each target platform you selected in the wizard, there will also be a project. These projects can be used to deploy and test the project on the individual platforms. In NetBeans each of the projects context menu has a submenu "custom" with entries that apply only to the deployment platform. For example the iOS project has an entry that allows you to run in an iPad simulator or deploy to a real iOS device, while the Android project has entries for running on an attached Android device.</p>
</div>
<div class="paragraph">
<p>In our tutorial, we have created a subproject that creates a NetBeans plugin. If you run it, it will start your plugin inside the core NetBeans Platform. It will also create an <code>nbm file</code> , that you can use to install the plugin in the IDE.</p>
</div>
<div class="paragraph">
<p>After the projects were created, NetBeans will immediately start an initial build and downloads all required dependencies.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_coding_the_module"><a class="anchor" href="#_coding_the_module"></a>Coding the Module</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In order to create our Github Tool, you need to complete the following steps:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#code-logic">Code the View Logic</a></p>
</li>
<li>
<p><a href="#code-view">Create the View</a></p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="_code_the_view_logic"><a class="anchor" href="#_code_the_view_logic"></a>Code the View Logic</h3>
<div class="paragraph">
<p>DukeScript allows developers a clean separation of view and view logic. The view is defined in HTML, the logic is written in Java. With this approach you can write and test the logic independent of the view. We&#8217;ll start by coding the logic of our application. The view will be added later.</p>
</div>
<div class="paragraph">
<p>In this section, you&#8217;ll use the <a href="https://bits.netbeans.org/html4j/1.0/net/java/html/json/Model.html">@Model</a> annotation to create the view logic of our application. You&#8217;ll learn how to connect a model to the Github REST API.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
In this section we will create a Java class with annotations. At compile-time, another Java class is generated from these annotations. This saves us a from writing a lot of setters, getters and other boilerplate code. If you&#8217;re interested in having a look at the generated code, you can find it in your project in Folder "Generated Sources (annotations)."
</td>
</tr>
</table>
</div>
<div class="olist arabic">
<ol class="arabic" start="1">
<li>
<p>In project "github General Client Code" &#8594; "Source Packages" open the class <code>DataModel.java</code> in the editor:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">package com.dukescript.github;
import net.java.html.json.Model;
@Model(className = "Data", targetId="", properties = {
})
final class DataModel {
private static Data ui;
/**
* Called when the page is ready.
*/
static void onPageLoad() throws Exception {
ui = new Data();
ui.applyBindings();
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We will use this class to define our view logic. The <code>Model</code> annotation defines the name of the model that will be generated, and it&#8217;s properties. Currently there are no properties.</p>
</div>
<div class="paragraph">
<p>In your project open <code>Generated Sources (annotations)</code> . You will find the class <code>Data.java</code> that has been generated from the Annotation.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="2">
<li>
<p>Change the value of attribute <code>className</code> to "ViewModel". If you save the file, NetBeans will show some errors. It will underline all uses of class <code>Data</code> . This is because the gerneated class is now named "ViewModel". Replace these occurrences with "ViewModel" to fix these problems.</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">package com.dukescript.github;
import net.java.html.json.Model;
@Model(className = "*ViewModel*", targetId="", properties = {
})
final class DataModel {
private static *ViewModel* ui;
/**
* Called when the page is ready.
*/
static void onPageLoad() throws Exception {
ui = new *ViewModel*();
ui.applyBindings();
}
}</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic" start="3">
<li>
<p>Now we&#8217;ll add a property of type String for the Github username:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import net.java.html.json.Model;
import net.java.html.json.Property;
@Model(className = "ViewModel", targetId="", properties = {
*@Property(name = "user", type=String.class)*
})
final class DataModel {
private static ViewModel ui;
/**
* Called when the page is ready.
*/
static void onPageLoad() throws Exception {
ui = new ViewModel();
ui.applyBindings();
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>When you save the file, it will automatically add a setter and a getter to the generated class. You can test it right away in method <code>onPageLoad</code> :</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> static void onPageLoad() throws Exception {
ui = new ViewModel();
*ui.setUser("dukescript");*
ui.applyBindings();
}</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic" start="4">
<li>
<p>Next we&#8217;ll Model the information we want to get from Github:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Model(className = "RepositoryInfo", properties = {
@Property(name = "id", type = int.class),
@Property(name = "name", type = String.class),
@Property(name = "owner", type = Owner.class),
@Property(name = "private", type = boolean.class)})
static class RepositoryModel {
}
@Model(className = "Owner", properties = {
@Property(name = "login", type = String.class)
})
static final class OwnerModel {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The names of these properties were not chosen at random. If you have a look at the JSON message from github, you&#8217;ll see that the structure and properties of our Model match some attribute names and the structure of the JSON message.</p>
</div>
<div class="paragraph">
<p>DukeScript can automatically parse JSON messages and map them to our Model class. That&#8217;s what we&#8217;ll utilize next.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="5">
<li>
<p>Add a new property to ViewModel:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import java.util.List;
import net.java.html.json.Model;
import net.java.html.json.OnReceive;
import net.java.html.json.Property;
@Model(className = "ViewModel", targetId = "", properties = {
@Property(name = "user", type = String.class),
*@Property(name = "repositories", type = RepositoryInfo.class, array = true)*
})
final class DataModel {
//... rest of the code</code></pre>
</div>
</div>
<div class="paragraph">
<p>Please note that the new property has the attribute <code>array</code> set to true. This means there can be multiple instances of RepositoryInfo.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="6">
<li>
<p>Now we&#8217;ll add a method to connect to the REST API:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> *@OnReceive(url = "https://api.github.com/users/{name}/repos")*
public static void connect(ViewModel vm, List&lt;RepositoryInfo&gt; repos) {
vm.getRepositories().clear();
vm.getRepositories().addAll(repos);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The method <code>connect</code> is annotated with the annotation <a href="https://bits.netbeans.org/html4j/1.0/net/java/html/json/OnReceive.html">@OnReceive</a> It will generate a new method of the same name in our ViewModel. The generated method has a parameter for the parts of the url enclosed in curly braces ({name}). When you call the method with a parameter, this will replace the <code>{name}</code> to build the URL dynamically.</p>
</div>
<div class="paragraph">
<p>The generated method does a lot of magic for us. It will call the URL to retrieve the answer. The answer is in the JSON format you&#8217;ve seen above. In our connect method, we&#8217;ve declared that we want to receive a <code>List&lt;RepositoryInfo&gt; repos</code> . Therefore the generated method will automatically parse the JSON message and map it to a list of RepositoryInfo Objects.</p>
</div>
<div class="paragraph">
<p>That&#8217;s probably the easiest way to parse a JSON message.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="7">
<li>
<p>Now we&#8217;ll declare a function that calls our generated method:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">*@Function*
public static void loadRepos(ViewModel vm){
vm.connect(vm.getUser());
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The annotation <a href="images/images">@Function</a> makes this method callable from the view. For example from a button click. We&#8217;ll use it in the next section.</p>
</div>
<div class="paragraph">
<p>In this section we have created the view logic of our application. We used the <code>@Model</code> annotation to generate several model classes, and we&#8217;ve used <code>@OnReceive</code> to connect to the Github REST API and read the repositories of a user. In the next section we&#8217;ll create a view for our plugin.</p>
</div>
</div>
<div class="sect2">
<h3 id="_create_the_view"><a class="anchor" href="#_create_the_view"></a>Create the View</h3>
<div class="paragraph">
<p>So far we created the view logic. Now we&#8217;re ready to create the view. In DukeScript the view is defined as HTML. Inside of the HTML we use some special <code>data-bind</code> attributes. With these attributes we establish a two-way binding between the view and the viewmodel.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="1">
<li>
<p>Open file <code>index.html</code> in the editor ( Web Pages | pages | index.html ). This is the (empty) view definition:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-html hljs" data-lang="html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Github Repositories&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic" start="2">
<li>
<p>Add a text input field, a button and a list:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-html hljs" data-lang="html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Github Repositories&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
&lt;/head&gt;
&lt;body&gt;
*&lt;input type="text" &gt;
&lt;button &gt;Load&lt;/button&gt;
&lt;ol &gt;
&lt;li&gt;&lt;span &gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;*
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="olist arabic">
<ol class="arabic" start="3">
<li>
<p>To make this dynamic you need to bind the elements to the model using <code>data-bind</code> attributes:</p>
</li>
</ol>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-html hljs" data-lang="html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Github Repositories&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;input type="text" *data-bind="textInput: user"*&gt;
&lt;button *data-bind="click: loadRepos"*&gt;Load&lt;/button&gt;
&lt;ol *data-bind="foreach: repositories"*&gt;
&lt;li&gt;&lt;span *data-bind="text: name*"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>The first binding <code>textInput</code> binds the value of the input to the property <code>user</code> of our model. Whenever the user types something in this textfield, the property <code>user</code> will be updated and vice versa. The <code>click</code> -binding on the button binds the click event to the function <code>loadRepos</code> . When the user clicks the button the method <code>loadRepos</code> will be called. The <code>foreach</code> -binding is bound to the property <code>repositories</code> of our model. For each entry in the array, it will copy the content of the enclosing element. That means, for each entry it will create a list item. For this list item the <code>RepositoryInfo</code> will be the binding context.</p>
</div>
<div class="paragraph">
<p>That&#8217;s the reason why here we can bind directly to the properties of <code>RepositoryInfo</code> . The <code>text</code> -binding simply binds the text of the span to the name property of the <code>RepositoryInfo</code> .</p>
</div>
<div class="paragraph">
<p>For more information on the binding syntax, check out the <a href="https://dukescript.com/knockout4j.html">documentation</a>. There&#8217;s a comprehensive list of all available bindings.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="4">
<li>
<p>Now run your application. When it runs enter a valid username in the input field. The ui will display a list of their repositories:</p>
</li>
</ol>
</div>
<div class="imageblock">
<div class="content">
<img src="../../_images/tutorials/dukescript_repos.png" alt="dukescript repos">
</div>
</div>
<div class="paragraph">
<p>1.
As a final step you should now run the application as a NetBeans Plugin. Switch to the project "github Client for NetBeans" and choose <code>run</code> .</p>
</div>
<div class="imageblock">
<div class="content">
<img src="../../_images/tutorials/dukescript_81_repos-plugin.png" alt="dukescript 81 repos plugin">
</div>
</div>
<div class="paragraph">
<p>In this section you&#8217;ve learned how to define a view in HTML and how to bind it to the view model. The view is still very basic, but you can use CSS (and even JavaScript) to make it look nicer. The HTML-renderer we use is based on Webkit and it&#8217;s pretty capable. We&#8217;ll cover this in another tutorial.</p>
</div>
<div class="paragraph">
<p>You&#8217;ve reached the end of this tutorial. We hope you like this modern way for separating the view from the view model. By the way, you can also create applications for Android, iOS, and the Browser with this technology.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_see_also"><a class="anchor" href="#_see_also"></a>See Also</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This concludes the NetBeans Platform Plugins with DukeScript. This document has described how to create a plugin that displays github repositories for any github user. For more information about creating and developing applications with DukeScript, see the following resources:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://dukescript.com">The DukeScript Website</a></p>
</li>
<li>
<p><a href="https://dukescript.com/blog.html">The DukeScript Blog</a></p>
</li>
<li>
<p><a href="https://bits.netbeans.org/html+java/1.2.3/index.html">NetBeans html4JavaAPI Javadoc</a></p>
</li>
</ul>
</div>
</div>
</div>
<section class='tools'>
<ul class="menu align-center">
<li><a title="Facebook" href="https://www.facebook.com/NetBeans"><i class="fa fa-md fa-facebook"></i></a></li>
<li><a title="Twitter" href="https://twitter.com/netbeans"><i class="fa fa-md fa-twitter"></i></a></li>
<li><a title="Github" href="https://github.com/apache/netbeans"><i class="fa fa-md fa-github"></i></a></li>
<li><a title="YouTube" href="https://www.youtube.com/user/netbeansvideos"><i class="fa fa-md fa-youtube"></i></a></li>
<li><a title="Atom Feed" href="https://netbeans.apache.org/blogs/atom"><i class="fa fa-mf fa-rss"></i></a></li>
<li><a title="Slack" href="https://tinyurl.com/netbeans-slack-signup/"><i class="fa fa-md fa-slack"></i></a></li>
<li><a title="Issues" href="https://github.com/apache/netbeans/issues"><i class="fa fa-mf fa-bug"></i></a></li>
</ul>
<ul class="menu align-center">
<li><a href="https://github.com/apache/netbeans-antora-tutorials/edit/main/modules/ROOT/pages/tutorials/nbm-dukescript.adoc" title="See this page in github"><i class="fa fa-md fa-edit"></i> See this page in GitHub.</a></li>
</ul>
</section>
</div>
<div class='grid-container incubator-area' style='margin-top: 64px'>
<div class='grid-x grid-padding-x'>
<div class='large-auto cell text-center'>
<a href="https://www.apache.org/">
<img style="height: 60px" title="Apache Software Foundation" src="../../../../_/images/asf_logo_wide.svg" />
</a>
</div>
<div class='large-auto cell text-center'>
<a href="https://www.apache.org/events/current-event.html">
<img style="width:234px; height: 60px;" title="Apache Software Foundation current event" src="https://www.apache.org/events/current-event-234x60.png"/>
</a>
</div>
</div>
</div>
<footer>
<div class="grid-container">
<div class="grid-x grid-padding-x">
<div class="large-auto cell">
<h1><a href="../../../../front/main/about">About</a></h1>
<ul>
<li><a href="../../../../front/main/community/who">Who's Who</a></li>
<li><a href="https://www.apache.org/foundation/thanks.html">Thanks</a></li>
<li><a href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a></li>
<li><a href="https://www.apache.org/security/">Security</a></li>
</ul>
</div>
<div class="large-auto cell">
<h1><a href="../../../../front/main/community">Community</a></h1>
<ul>
<li><a href="../../../../front/main/community/mailing-lists">Mailing lists</a></li>
<li><a href="../../../../front/main/community/committer">Becoming a committer</a></li>
<li><a href="../../../../front/main/community/events">NetBeans Events</a></li>
<li><a href="https://www.apache.org/events/current-event.html">Apache Events</a></li>
</ul>
</div>
<div class="large-auto cell">
<h1><a href="../../../../front/main/participate">Participate</a></h1>
<ul>
<li><a href="../../../../front/main/participate/submit-pr">Submitting Pull Requests</a></li>
<li><a href="../../../../front/main/participate/report-issue">Reporting Issues</a></li>
<li><a href="../../../../front/main/participate/#documentation">Improving the documentation</a></li>
</ul>
</div>
<div class="large-auto cell">
<h1><a href="../../../../front/main/help">Get Help</a></h1>
<ul>
<li><a href="../../../../front/main/help/#documentation">Documentation</a></li>
<li><a href="../../../../wiki/main/wiki">Wiki</a></li>
<li><a href="../../../../front/main/help/#support">Community Support</a></li>
<li><a href="../../../../front/main/help/commercial-support">Commercial Support</a></li>
</ul>
</div>
<div class="large-auto cell">
<h1><a href="../../../../front/main/download">Download</a></h1>
<ul>
<li><a href="../../../../front/main/download">Releases</a></li>
<li><a href="https://plugins.netbeans.apache.org/">Plugins</a></li>
<li><a href="../../../../front/main/download/#_daily_builds_and_building_from_source">Building from source</a></li>
<li><a href="../../../../front/main/download/#_older_releases">Previous releases</a></li>
</ul>
</div>
</div>
</div>
</footer>
<div class='footer-disclaimer'>
<div class="footer-disclaimer-content">
<p>Copyright &copy; 2017-2025 <a href="https://www.apache.org">The Apache Software Foundation</a>.</p>
<p>Licensed under the Apache <a href="https://www.apache.org/licenses/">license</a>, version 2.0</p>
<div style='max-width: 40em; margin: 0 auto'>
<p>Apache, Apache NetBeans, NetBeans, the Apache feather logo and the Apache NetBeans logo are trademarks of <a href="https://www.apache.org">The Apache Software Foundation</a>.</p>
<p>Oracle and Java are registered trademarks of Oracle and/or its affiliates.</p>
<p>The Apache NetBeans website conforms to the <a href="https://privacy.apache.org/policies/privacy-policy-public.html">Apache Software Foundation Privacy Policy</a></p>
</div>
</div>
</div>
<script src="../../../../_/js/vendor/lunr.js"></script>
<script src="../../../../_/js/search-ui.js" id="search-ui-script" data-site-root-path="../../../.." data-snippet-length="100" data-stylesheet="../../../../_/css/search.css"></script>
<script async src="../../../../search-index.js"></script>
<script src="../../../../_/js/vendor/jquery.min.js"></script>
<script src="../../../../_/js/vendor/what-input.min.js"></script>
<script src="../../../../_/js/vendor/foundation.min.js"></script>
<script src="../../../../_/js/vendor/jquery.colorbox-min.js"></script>
<script src="../../../../_/js/netbeans.js"></script>
<script>
$(function(){ $(document).foundation(); });
</script>
<script src="../../../../_/js/vendor/highlight.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('pre code').forEach((el) => {
hljs.highlightElement(el);
});
});
</script>
</body>
</html>