Creating a Vue.JS Instance Using its Fundamental Concepts

Vue.js is simple and powerful, and it is easy to learn. Once you understand the basics of the framework, everything will work in just the way you expect. The framework will help you to keep focused on writing the logic of your application instead of remembering a bunch of APIs that are hard to use. This article will help you learn some of the fundamental concepts in Vue.js.

Fundamental concepts

When you start a Vue application, what you need to keep in mind is your application’s logic itself. You don’t need to remember a set of APIs so that you can connect different pieces of your code. Vue.js, which is a progressive framework, provides you with an intuitive way of writing web applications, starting small and growing incrementally into a large-scale application. If you have used other frameworks before, you may wonder why they make things unnecessarily complicated. Now, let’s go through fundamental concepts in Vue.js and create a sample application. You can also access the complete code for this article at https://github.com/PacktPublishing/Building-Applications-with-Spring-5-and-Vue.js-2/tree/master/Chapter02/MessagesApp.

Creating a Vue instance

Creating a Vue instance is the start of every Vue.js application. Typically, a Vue application consists of two types of Vue instance—the root Vue instance and component instances. You create the root instance with the Vue function, as follows:

new Vue({/* options */});

The options object here is where you describe your application. Vue.js takes this object and initializes the Vue instance. Let’s create a simple application, called the Messages App and see how to use the options object. This SPA has the following features:

  • Add a message
  • View messages list
  • Delete a message
  • Automatically disable the add feature under certain conditions

We will start by creating the index.html file and, from there; we will build our application incrementally. Let’s have a look at the index.html file:

1.<!DOCTYPE html>
2.<html>
3.<head><title>Messages App</title></head>
4.<body>
5.
6.https://unpkg.com/vue@2.5.13/dist/vue.js 7. 8.let vm = new Vue({ 9.el: '#app' 10. }); 11. 12. </body> 13. </html>

In line 5, we create a 

 element with an app id in the DOM. And in line 9, we mount our application to this element by using the el property of the options object. el is short for element, and its value can be a CSS selector string, like the one we use here, '#app', or it can be the HTMLElement itself, document.getElementById('app'). In line 8, we assign the Vue instance to the vm variable, which is short for ViewModel.

Now, let’s define our data model of the application. We need an array to hold those added messages and a string to bind to the form’s input which will accept new messages. Here is how the data object appears:

...
let vm = new Vue({
el: '#app',
data: {
    messages: [],
newMessage: ''
}
});
...

We add the data object using object literals. As you can see, it is quite straightforward. We give them initial values so that you can easily tell that messages is an array and newMessage is a string. Providing initial values for the data object properties is a good practice. It not only helps you understand the data model better, but also makes those properties reactive by default. 

Besides using a plain object as the value of the data property of the options object, you can also use a function that returns a plain object, as in the example:

...
data () {
  return {
    messages: [],
    newMessage: ''
  }
}
...

Using a function is required when you define the data structure for a component because, in that way, Vue.js will always create a fresh data model for the new component. If you use a plain object to define a component’s data model, all of the instances of that component will share the same data object, which is not desired. For our root Vue instance here, we are safe to use a plain object.

For now, we have only defined the data model, and we haven’t told Vue.js what to do with the data object. Let’s add a template for displaying and adding messages. You can add a template in three ways. One is to add an inline template string using the template property of the options object. It is appropriate to adopt this approach when you create a component that doesn’t have a lot of markups.

The second way is to put the template directly inside the mounting point, 

. Vue.js will parse the template inside #app and replace it with HTML generated by Vue.js. The third way is to put the template markup inside a script tag, for example, <script type="x-template" id="tmplApp">, and put '#tmplApp' as the value of the template property of the options object. We will adopt the second approach here just so we can have the template markup close to the final output. Here is how the template appears:

... 
5.
6.
    7.
  • 8. {{ message.text }} - {{ message.createdAt }} 9.
  • 10.
11. 12. 14.
15. </form> 16. </div> ...

In line 7, we use the Vue built-inv-for directive to render the messages list. The syntax of the v-for directive is alias in source. In our code, message is alias and messages is source. We don’t need to write vm.messages in order to access the messages property. Just use the exact name that you put in the data object. And by adding the v-for directive to the li tag, we create a v-for block inside the li tag, and that’s where thealias message will be available. You can think of the v-for block as being equivalent to the for-loop block in JavaScript.

In line 8, we use Mustache syntax to output the text property and createdAt property of a message object of the messages list. The createdAt property is a Date object that we add when saving a new message. When Vue.js parses the template and interpolates a Mustache tag, for example, {{message.text}}, it creates data binding between the output and the data. It will replace the tag with the actual value and update the output whenever the text property has been changed. The text interpolation also supports JavaScript expression. For example, you can make the text property always in lower case with {{message.text.toLowerCase()}}.

In line 11, we use another built-in directive, v-on, to attach an event listener to the form’s submitevent.prevent is a modifier, telling Vue.js to call event.preventDefault() so that the browser won’t actually submit the form. addMessage is a method that will be invoked when the form’s submit event is triggered. We will create this method shortly. You can use v-on to attach listeners to all of the normal DOM events, for example, click and mouseover. You can also use it to listen to custom events of Vue’s custom components in the same way. We will see how that works shortly.

In line 12, we use the built-inv-model directive to create a two-way binding between the textarea element and newMessage property of the data object. In this way, whenever the value of the textarea element is changed, the newMessage will be updated automatically. And when newMessage is changed, textarea will be updated accordingly. This is such a nice feature that you can get the value of the textarea element without touching it yourself specifically. It works just as you would imagine it should.

In line 14, we add a button with type="submit" to trigger the submit event of the form. Now, let’s create our addMessage method to listen to that event. We can do it by using the methods property of the options object.

Here is how the options object appears with the addMessage method:

...
let vm = new Vue({
...
data: {
...
},
methods: {
addMessage (event) {
if (!this.newMessage) {return;}
this.messages.push({
text: this.newMessage, createdAt: new Date()});
this.newMessage = '';
}
}
});
...

The methods property of the options object takes an object, where you put all of your methods. And inside these methods, you have access to the properties of the data object via this, as you can see that we use this.newMessage and this.messages inside the addMessage method to access them. The method syntax we use here is ES6, but you can also use function expression, as follows:

addMessage: function (event) {
  // Logic goes here
}

However, you should not use arrow functions syntax to create methods because you will lose access to the Vue instance via this.

Inside the addMessage method, we add the new message to the messages array using the push() method, and then we reset the newMessage property. Accordingly, Vue.js will clear textarea in the UI automatically. This is the magic of two-way binding, which will be revealed soon.

Now, let’s add a way to delete a message from the UI. Here is what we change in the template:

...
<li v-for="message in messages">
{{ message.text }} - {{ message.createdAt }} 
<button @click="deleteMessage(message)">X</button>
</li>
...

We add a button and use @click, the short-hand of v-on:click, to attach the listener deleteMessage method to the click event. Instead of putting the method’s name here, we use an inline statement to pass the message object to the method. And here are the updated methods of the options object:

...
let vm = new Vue({
... 
methods: {
...
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
});
...

We delete the selected message from the messages array using the Array.prototype.splice() method. Vue.js will detect this change and update the DOM automatically. You don’t need to manipulate the DOM at all.

Now, let’s add the ability to automatically disable the add feature. Let’s say we want to disable the Add button when there are 10 messages in the list. To do that, we can use the built-in v-bind directive to bind the Add button’s disabled attribute with the messages.length >= 10 expression. In this way, Vue.js will update the disabled attribute automatically when the length of the messages array changes. Here is the updated template:

...
<form @submit.prevent="addMessage">
...
</form> ...

What if we want to change the logic so that the Add button is disabled when the length of the textarea input exceeds 50 characters? You will need to change the value of the v-bind directive to newMessage.length > 50. What if we want to disable the button when there are already 10 messages, or the length of newMessage exceeds 50 characters? We can change the directive value to messages.length >= 10 || newMessage.length > 50. It still works. However, as you can see, the code starts to bloat and it would become hard to maintain when you need to add more logic to decide when the Add button should be disabled.

Here, we can use computed properties. As the name suggests, the value of such a property is computed rather than defined as those in the data object. And Vue.js will track the dependencies of a computed property and update the property’s value when the dependencies change. Let’s add the computed property addDisabled to the options object:

let vm = new Vue({
data {
...
},
computed: { 
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50;
}
},
... 
});

As you can see, the addDisabled computed property is defined as a method of the computed object of the options object. Inside the method, you also have access to the Vue instance via this. For the v-bind directive, there is also a shorthand option, which is a colon (:). Let’s update the Add button in the template to the following:

<button :disabled="addDisabled" type="submit">Add</button>

As you can see, our template becomes much easier to follow and maintain since you keep most of the logic in the JavaScript rather than in the HTML template. 

For the v-bind directive, you can use it to bind the HTML element’s built-in attributes, for example, class and style. You can also use it to bind a Vue’s custom component property. We will see how that works shortly.

By now, we have implemented all of the features of the Messages App. Since we didn’t use <script type="module">, you can open index.html directly using Chrome. If you try it now, you will see something strange. Immediately after opening the file, you can see the template markups that we put inside the mounting point, 

, which is awkward. The reason that it behaves in this way is that, before the browser loads Vue.js and executes it, it will display the HTML in the manner it is defined until Vue.js takes control of the DOM and removes the template markups from the mounting point and then replaces it with the new dynamically generated DOM. Let’s fix this by adding the v-cloak directive to the mounting point and inserting a CSS rule to hide the template markups. Vue.js will remove the v-clock directive when the generated DOM is ready. The following are the updates to the index.html file:


...

[v-cloak] {display: none;}
body > div {width: 500px; margin: 0 auto;}
textarea {width: 100%;}
ul {padding: 0 15px;}



...
</body>

Besides the [v-cloak] CSS rule, we add a few other rules to style the UI a little bit, even though it is still very primitive with these rules. Now, if you open it again in the browser, there is no flash anymore. 

By now, you’ve learned how to use the data object, the computed object, and the methods object of the options object of a Vue instance. And you can see that, even though the properties of these objects are defined separately, you can access them in the same way, which is via this

Now, let’s open the Console tab of Chrome’s Developer tools. Instead of using the input field in the UI, let’s add a new message from the console by interacting directly with the vm object, which is the root Vue instance that we created and made available in the global scope. As you can see from the following screenshot, it works as you would expect. This is its simplicity and powerfulness:


The Messages App in Chrome

Apart from the datacomputed, and methods properties, the options object has many other properties that you can use to define a Vue instance.

If you found this article interesting, you can explore Building Applications with Spring 5 and Vue.js 2 to Become efficient in both frontend and backend web development with Spring and Vue. With the help of Building Applications with Spring 5 and Vue.js 2 you’ll get to grips with Spring 5 and Vue.js 2 as you learn how to develop a web application.

Creating and Editing Content in Drupal

Drupal sites are all about content. In order for the site to present content to the visitor, it needs to contain content. How does content get into the Drupal site? In this article, you will learn how to work with content, create it and edit it. So, let’s get started!

Using the WYSIWYG editor

When you send a text message from your phone, you type the text, and that’s pretty much all there is to it in terms of the content’s appearance. You’re limited to choosing whether you want to use capital letters. If you want more control over your message, you can switch to sending an email message, which might enable you to select bold, italic, or underlined text.

If you want to send highly formatted content, text with headings, font changes, text color, bulleted lists, and so forth, you might use word processing software, such as Word, Pages, or LibreOffice. 

This class of software creates what is called rich text, that is, text rich in styling, and the generic term for software capable of creating a document containing rich text is a rich text editor

Drupal 8 includes a rich text editor called CKEditor. It is a highly configurable editor with a composition area, as shown in the following screenshot, that resembles standalone word processors with formatting buttons. This type of editor is referred to as WYSIWYG, which is pronounced as wizzy-wig, which stands for What You See Is What You Get, meaning that the text it displays shows the selected formatting:

Let’s create a piece of content and reproduce what is in the image. Click on Content in the admin menu, then click on the +Add content button. When you are presented with the list of content types, click Article.

The page contains all the fields and settings necessary for creating Article content. Those fields that require an entry have an asterisk following their name, and I will also include an asterisk as we address each of the fields, in turn.

Title*

The Title is a required field for all content because it is used to identify one piece of content as distinct from another, though it does not have to be unique, and so more than one piece of content can have the same title, which quickly becomes confusing. The entry in this field will appear in the summary admin list of content. It is also used as the page title when the content is read on its own page. Therefore, it is best to enter a title that is both meaningful to you and informative to the site visitor. Enter one now. Mine will be A Moment in the Life of My Dog.

Body

You might be wondering why the Body field is not required since the article would be somewhat useless without body text. It is because you might want to create the article as a draft and return later to actually create the body text. This is not possible with the title, because without one, it would be difficult to find the article.

Summary Field

Next to the field name is a link that reads Edit Summary. When creating a content type, the creator decides whether the body text field should accommodate having a separate summary and if so, that link is shown. The summary is used, for example, when providing a list of content for the site visitor, where a short description of the content—a teaser—is provided.

There are two ways to provide the teaser text: via a separate summary or by trimming, that is, excerpting text from the start of the content. I prefer to use the summary because trimmings are based on a set number of characters that is the same for all of the same type of content, such as the first 40 characters, so I have no control over where the excerpt will end… maybe it will end in the middle of a word. With a summary, I can precisely control what will be shown. Even if it is identical to the start of my body text, I am able to ensure that it ends in an appropriate place.

Click on the Edit Summary link. A Summary text box will appear above that for the body. It contains a link to hide the field again. This toggle of Edit summary/Hide summary allows you to remove its distraction until you need to use it. You will notice that the bottom right corner of the text box has a widget that’s meant to resemble page corners that can be grabbed with the mouse and dragged to enlarge or reduce the size of the text box. It also has a note below it letting you know that if you do not enter summary text into the field, Drupal will use a trimmed version of the body text as a summary. Enter some summary text into the field. Mine will read: Sasha makes an exciting discovery.

Body text

In the Body field goes the full text of the article. Depending on the text format selected, various buttons are available from the editor for formatting the text. My text looks as follows:

Today Sasha Gabor discovered donuts! They’re not exactly healthy diet fare for a dog, but she didn’t seem to mind.

Enter some text for your article, and format some of it using the B (bold) or I (italic) buttons. When you have entered it, click the Source button to see what the actual HTML markup looks like. Mine shall look like this:

<p>Today Sasha Gabor discovered <strong>donuts</strong>! They're not <em>exactly</em> healthy diet fare for a dog, but she didn't seem to mind.</p>

If you ever need to customize the underlying HTML of the body text, this is where you can do it. Click the Source button again to return to the WYSIWYG mode.

Text format

The Text format dropdown is used for selecting the filter that will be used when entering text. Security is a constant concern on websites that accept entry from users, as some bad actors will attempt to enter markup that can cause havoc. Filtering which type of markup is allowed helps to mitigate the potential for trouble.

These filters can be defined and configured, and the configuration will, for example, identify which HTML tags will be allowed and which processes will be run (such as HTML correction) and in which order. Filters are assigned to user roles, and a user role can have access to more than one filter. The filters that are included with Drupal and their default configurations are as follows:

  • Plain text: No HTML tags will be accepted
  • Restricted HTML: A minimal set of HTML such as italics, bold, and headings are typically used for anonymous users (users who are not logged in)
  • Basic HTML: Similar to Restricted HTML, but usually inclusive of a configuration for WYSISYG toolbar buttons, since the filter is normally used for authenticated (logged in) users who will have access to the editor
  • Full HTML: Allows any valid HTML tag, and should be assigned with care

 This filter will not be included in the dropdown as it is used when no other filters are available.

Tags

The Tags field is used to include terms related to the content. These terms can be used by users to search for content. Multiple terms should be separated by commas. The field is an auto-complete field, meaning that any existing terms matching the characters typed will be offered for you to click rather than typing the entire term. My tags will be Sasha and donuts.

Images

The Image field enables choosing a file from the device you are using and uploading it to be stored and associated with the content. The maximum size of the file is given below the field (typically 2 MB) and so are the file types that will be accepted. I’m going to add an image and enter Sasha and her donut as alt text. Alt text is used to provide information about the image to those users using a screen reader or some other form of access rather than viewing the image.

Publishing the content

The Published checkbox sets the content’s status to be either draft (unchecked) or published (checked). Typically, only editors, admins, and the content’s author will be allowed to view the content when in its draft state. The content will not appear in menus or lists to those roles that don’t have permission to view it. Let’s check the box to publish our content.

Additional settings

That covers the main fields used to create Article content. There are additional configuration options available via the vertical tabs. Let’s take a look at those:

These additional settings are referred to as metadata, which is information used to describe the content that is not actually part of the content.

Revision log message

If the content type that you are working with is set for revisions, you can enter a description of the changes you have made to the content in this box, and that description will be saved and listed when reviewing the available revisions for this content.

Menu Settings

Should you want a menu link for this content, checking this box will open a dialog for providing the following settings:

  • Menu link title: The text that appears as a link
  • Description: The text shown when the mouse hovers over the link
  • Parent item: The menu link under which this one will be indented
  • Weight: A value which determines the position of this menu item in relation to any others (based on their weights) with the same parent

Comment Settings

If your content type is set to allow comments, you can set this particular piece of content to accept comments (open) or not (or no longer) accept comments (closed).

URL Path Settings

When a new piece of content is created, it is assigned a sequential numeric ID known as the node ID or NID. By default, the URL displayed when the content is presented, similar to http://mysite.com/node/123, where 123 is the NID. If you want your content to have a more meaningful URL than node/123, such as sasha-discovers-donuts, that custom URL, known as an alias, can be defined here.

Authoring Information

If you are creating content on behalf of another user, you can begin typing their username in the text box in this tab and choose the name from the presented matches, as well as specify the date that should be considered the content creation date.

Promotion Options

Content can be “promoted” to a higher status for the purposes of including it on the home page or even featuring it there.

  • Promoted to front page: Some sites determine what content is displayed on the front page by selecting only that content that has been promoted.
  • Sticky at top of lists: If the content is Sticky, it will stay on the homepage while other content is replaced with newer content. This is a good way to have a welcome message remain while other content is cycled.

Completing the process

At this point, we are ready to save our new content by clicking on the Save button. After doing so, the content is displayed for you to see. Note the URL that is shown. In my case, it is node/2. I would rather have something more meaningful for the user and for good SEO, so I will click the Edit tab and navigate to the vertical tab for URL PATH SETTINGS. There, I will enter /sasha-discovers-donuts (the initial slash is required) and save the article again. Now, the browser will show my improved URL. I can still use the original URL of node/2. It has not been removed. Create a better URL for your content in the same way.

You will notice that one of the tabs above your article reads Revisions. Click on it. Because the Article content type is configured to create revisions when saved, and we did not override that by unchecking the Create new revision checkbox, the change we made and saved in order to improve our URL resulted in the change being saved as a new version rather than simply overwriting the original one. In this manner, we could create a new revision every time we edit the content. From this Revisions page, we can do the following:

  • View a list of the previous versions
  • View a previous version
  • Delete a previous version
  • Revert to (restore) an earlier version, foregoing the changes made since

If you found this article interesting, you can explore Drupal 8 Quick Start Guide for your step by step guide with easy to follow instructions for navigating Drupal 8. Drupal 8 Quick Start Guide will clear your path from installation to a building usable site in minutes, and to a customized site in one sitting.

Components in Blazor

Blazor is a component-based framework. In that component is defined as a block of the UI, consisting of both HTML and the corresponding business logic. The HTML helps to render the component on the web page, and the logic handles the database operations or the event handling. A Blazor component is lightweight, flexible, and shareable across different projects.

The bottom line is that all UI fragments can be termed components in Blazor.

Creating a component in Blazor

We will now discuss the following two methods for creating components in Blazor:

  • Using a single file
  • Using a code-behind file

Let’s examine both of them in detail in this section.

Using a single file

We will use a single file with the .cshtml extension to create our component. To create a component file, right-click on the Pages folder of your BlazorDemo project and select New File. Type in the filename as CompDemo.cshtml and press Enter to create the file.

Put the following lines of code inside this file:

@page "/singlepagecomp"

<h1>@PageTitle</h1>
<hr/>
<p>This component is created using a single .cshtml page.</p>

@functions {
string PageTitle = "Component Demo";
}

Both the HTML and the @functions section are defined in only one file, that is, CompDemo.cshtml. Here, we have defined a PageTitleproperty to set the title on the page. On execution, this page will show a heading and a sample message, as defined in this property. But before running this application, we need to add the navigation link to this page to the \Shared\NavMenu.cshtml file. Open the \Shared\NavMenu.cshtml file and add the following code to it:

<li class="nav-item px-3">
  <NavLink class="nav-link" href="singlepagecomp">
    <span class="oi oi-list-rich" aria-hidden="true"></span> Comp Demo
  </NavLink>
</li>

This will add a Comp Demo navigation menu item, which will redirect to the CompDemo.cshtml page when clicked.

Type the dotnet run command into the VS Code console and press Enter. Open the URL in the browser, and you should see a page similar to one shown in the following screenshot:

You can observe that we have a Comp Demo link in the navigation menu on the left. Click on it to navigate to the CompDemo.cshtml component. It should open a page like the one shown in the following screenshot:

You can observe that the route URL of the page has /singlepagecomp attached to it, and that the message is being displayed on the page as we defined it in our component.

Using a code-behind file

In this method, we will be using two files to create our component—one file to hold the HTML part of the component and another to hold the logic part of the component.

To add the files, we will follow the same process that we employed earlier. Right-click on the Pages folder and select New File. Name the file CodeBehindComp.cshtml and press Enter to create the file. This file will contain the HTML section of our component. Similarly, add one more file, CodeBehindComp.cshtml.cs, to the Pages folder. This file will contain our logic section, which will define the members of the component class.

Open CodeBehindComp.cshtml.cs and put the following code into it:

using Microsoft.AspNetCore.Blazor.Components;

namespace BlazorDemo.Pages
{
    public class CodeBehindCompModel : BlazorComponent
    {
        public string PageTitle { get; set; } = "Component Demo";
    }
}

Here, we have defined a CodeBehindCompModel class that contains a PageTitle string property, which sets the title of the component once it is rendered as a web page in the browser.

Note that the Blazor compiler generates classes for all of the view pages with the same name as the page name; hence, we have suffixed the class name with the word “model” to distinguish it from the page name. If we use the same class name as page name (CodeBehindComp, in this case), then it will result in a compile time error.

Open CodeBehindComp.cshtml and put the following code into it:

@page "/codebehindcomp"
@inherits CodeBehindCompModel

<h1>@PageTitle</h1>

<p>This component is created using two files,.cshtml and.cshtml.cs</p>

This page will inherit the class defined in our code-behind page by using the @inherits directive. This allows us to use all of the properties and methods defined in the class from this page.

Add the navigation link for this page, as defined in the following snippet, inside the \Shared\NavMenu.cshtml file:

<li class="nav-item px-3">
  <NavLink class="nav-link" href="codebehindcomp">
    <span class="oi oi-list-rich" aria-hidden="true"></span> Code Behind Comp
  </NavLink>
</li>

Execute the application by running the dotnet run command, and click on the Code Behind Comp link in the navigation menu on the left. You should see a page similar to the one shown in the following screenshot:

Here, the title of the page is set to Component Demo because of the PageTitle variable defined in the code-behind file, whereas the messages is displayed using the HTML defined in the .cshtml file.

Using a component within another component

The Blazor framework also allows us to use a component within another component. This will work like a parent-child relationship, where the parent component can refer to the child component.

We will demonstrate this concept with the help of an example.

Create two files in the Pages folder, and name them ParentComp.cshtml and ChildComp.cshtml.

Open the ChildComp.cshtml page and put the following code into it:

<hr/>
<h3> Welcome to the Child Component</h3>

@ChildContent
@functions { [Parameter] private RenderFragment ChildContent { get; set; } }

Here, we first defined some dummy messages to be displayed on the page. There is no route defined for this component, as it will act as a child component and will be referred to by another component. The parent component will pass the content to the child component so that it can be rendered in a <div> tag. We will use a RenderFragment property, ChildContent, to hold the message supplied by the parent component. ChildContent is a component parameter decorated by the [Parameter] attribute. RenderFragment is defined in the application metadata, and represents a segment of the UI content, implemented as a delegate that writes the content to an instance of Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder.

The component parameter must fulfill the following two criteria:

  • It must be a non-public property
  • The component parameter that will receive the RenderFragment content must be named ChildContent

Open ParentComp.cshtml and enter the following code:

@page "/ParentComponent"

<h1>Parent-child example</h1>

<ChildComp>
    This is parent component data.
</ChildComp>

We defined the route of this application at the top of the preceding snippet as /ParentComponent. To refer to the child component, we use a tag with the same name as the file name of the child component, which is <ChildComp>in this case. The RenderFragment parameter is provided between the tags of the child component. In this case, we provide a string message that will be rendered by the child component.

Before executing the code, we need to add the following navigation link of the parent component to the \Shared\NavMenu.cshtml file:

<li class="nav-item px-3">
  <NavLink class="nav-link" href="ParentComponent">
    <span class="oi oi-list-rich" aria-hidden="true"></span> Parent-Child
  </NavLink>
</li>

Run the application and click on the Parent-Child link in the navigation menu. You should see a page similar to the following screenshot:

You can see the content of the parent component, along with that of the child component, displayed on the page.

If you found this article interesting, you can explore Blazor Quick Start Guide to work with the fundamentals of Blazor to create rich and interactive web application. Blazor Quick Start Guide introduces you to the core concepts of Blazor, and how to apply these to a real-world web app with the help of Entity Framework Core and SQL Server.

Reactive Programming in a Nutshell

In this guest post, Peter Verhas attempts to explain reactive programming with simple yet effective examples, which drive home the concept of reactive programming.

Reactive programming is a paradigm that focuses more on where the data flows during computation than on how to compute the result. The problem is best described as several computations that depend on the output of one another, but if several may be executed independently of the other, reactive programming may come into the picture. As a simple example, we can have the following computation that calculates the value of h from some given b, c, e,and f values, using f1, f2, f3, f4,and f5 as simple computational steps:

a = f1(b,c) 
d = f2(e,f) 
k = f3(e,c) 
g = f4(b,f,k) 
h = f5(d,a,g)

If we write these in Java in a conventional way, the methods f1 to f5 will be invoked one after the other. If we have multiple processors and we are able to make the execution parallel, we may also perform some of the methods parallel. This, of course, assumes that these methods are purely computational methods and do not change the state of the environment, and, in this way, they can be executed independently of one another. For example, f1, f2, and f3 can be executed independently of one another. The execution of the  f4function depends on the output of f3, and the execution of f5 depends on the output of f1, f2, and f4.

If we have two processors, we can execute f1 and f2 together, followed by the execution of f3, then f4, and, finally, f5. These are the four steps. If we look at the preceding calculation not as commands but rather as expressions and how the calculations depend on one another, then we do not dictate the actual execution order, and the environment may decide to calculate f1 and f3 together, then f2 and f4, and, finally f5, saving one step. This way, we can concentrate on the data flow and let the reactive environment act upon it without putting in extra constraints:

This is a very simple approach of reactive programming. The description of the calculation in the form of expressions gives the data flow, but in the explanation, we still assumed that the calculation is executed synchronously. If the calculations are executed on different processors located on different machines connected to a network, then the calculation may not and does not need to be synchronous.

Reactive programs can be asynchronously executed if the environment is asynchronous. It may happen that the different calculations, f1 to f4, are implemented and deployed on different machines.

In such a case, the values calculated are sent from one to the other over the network, and the nodes execute the calculation every time there is a change in the inputs. This is very similar to good old analog computers that were created using simple building blocks, and the calculations were done using analog signals.

The program was implemented as an electronic circuit, and when the input voltage or current (usually voltage) changed in the inputs, the analog circuits followed it at light speed, and the result appeared in the output. In such a case, the signal propagation was limited by the speed of light on the wires and analog circuitry speed in the wired modules, which was extremely fast and may beat digital computers.

When we talk about digital computers, the propagation of the signal is digital, and this way, it needs to be sent from one calculation node to the other one, be it some object in JVM or some program on the network. A node has to execute its calculation if either of the following apply:

  • Some of the values in the input have changed
  • The output of the calculation is needed

If the input has not changed, then the result should eventually be the same as the last time; thus, the calculation does not need to be executed again—it would be a waste of resources. If the result of the calculation is not needed, then there is no need to perform the calculation, even if the result would not be the same as the last one. No one cares.

To accommodate this, reactive environments implement two approaches to propagate the values. The nodes may pull the values from the output of other modules. This will ensure that no calculation that is not needed will be executed. The modules may push their output to the next module that depends on them. This approach will ensure that only changed values ignite calculation. Some of the environments may implement a hybrid solution.

When values change in the system, the change is propagated toward the other nodes that again propagate the changes to another node, and so on. If we imagine the calculation dependencies as a directed graph, then the changes travel towards the transitive closure of the changed values along the nodes connected.

The data may travel with all the values from one node output to the other node input, or only the change may travel. The second approach is more complex because it needs the changed data and also meta information that describes what has changed. On the other hand, the gain may be significant when the output and input set of data is huge, and only a small portion of it is changed.

It may also be important to calculate and propagate only the actual delta of the change when there is a high probability that some of the nodes do not change the output for many of the different inputs. In such a case, the change propagation may stop at the node where there is no real change in spite of the changed input values. This can save up a lot of calculation in some of the networks.

In the configuration of the data propagation, the directed acyclic graph can be expressed in the code of the program; it can be configured, or it can even be set up and changed during the execution of the code dynamically. When the program code contains the structure of the graph, the routes and the dependencies are fairly static.

To change the data propagation, the code of the program has to be changed, recompiled, and deployed. If there are multiple network node programs, this may even need multiple deployments that should be carefully furnished to avoid different incompatible versions running on different nodes.

There should be similar considerations when the graph is described in some configuration. In such a case, the compilation of the program(s) may not be needed when only the wiring of the graph is changed, but the burden to have compatible configuration on different nodes in the case of a network execution is still there.

Letting the graph change dynamically also does not solve this problem. The setup and the structure are more flexible and, at the same time, more complex. The data propagated along the edges of the graph may contain not only computational data but also data that drives changes in the graph. Many times, this leads to a very flexible model called higher-order reactive programming.

Reactive programming has a lot of benefits, but, at the same time, it may be very complex, sometimes too complex, for simple problems. It is to be considered when the problem to be solved can easily be described using data graph and simple data propagations. We can separate the description of the problem and the order of the execution of the different blocks. This is the same consideration that we discussed in the previous chapter. We describe more about the what to do part and less about the how to do part.

On the other hand, when the reactive system decides the order of execution, what is changed, and how that should be reflected on the output of other blocks, it should do so without knowing the core of the problem that it is solving. In some situations, coding the execution order manually based on the original problem could perform better.

Note

This is similar to the memory management issue. In modern runtime environments, such as the JVM, Python runtime, Swift programming, or even Golang, there is some automated memory management. When programming in C, the programmer has full control over memory allocation and memory release.

In the case of real-time applications, where the performance and response time is of the utmost importance, there is no way to let an automated garbage collector take time and delay the execution from time to time. In such a case, the C code can be optimized to allocate memory when needed; there is a resource for the allocation and release of memory when possible, and there is time to manage memory.

These programs are better performing than the ones created for the same purpose using a garbage collector. Still, we do not use C in most of the applications because we can afford the extra resources needed for automated memory collection. Even though it would be possible to write a faster code by managing the memory manually, automated code is faster than what an average programmer would have created using C, and also the frequency of programming errors is much lower.

Just as there are some issues that we have to pay attention to when using automated memory management, we have to pay attention to some issues in a reactive environment, which would not exist in the case of manual coding. Still, we use the reactive approach for its benefits.

The most important issue is to avoid loops in thedependency graph. Although it is absolutely perfect to write thedefinition of calculations, a reactive system would probably not be able tocope with these definitions. Some reactive systems may resolve in somesimple-case cyclic redundancy, but that is an extra feature, and we generallyjust have to avoid that. Consider the following computations:

a = b + 3 
b = 4 / a

Here, a depends on b, so when b changes, a is calculated. However, b also depends on a, which is recalculated, and, in this way, the system gets into an infinite loop. The preceding example seems to be simple, but that is the feature of a good example. Real-life problems are not simple, and in a distributed environment, it is extremely hard sometimes to find cyclic redundancy.

Another problem is called a glitch.Consider the following definition:

a = b + 3 
q = b + a

When the parameter b is changed, for example, from 3 to 6, the value of a will change from 6 to 9, and, thus, q will change from 9 to 15. This is very simple. However, the execution order based on the recognition of the changes may first alter the value of q from 9 to 12 before modifying it to 15 in the second step.

This can happen if the calculating node responsible for the calculation of q recognizes the change in b before the value of a as a consequence of the change in the value of b. For a short period of time, the value of q will be 12, which doesn’t match the previous one and the changed state. This value is only a glitch in the system that happens after an input changes and also disappears without any further change in the input in the system:

If you have ever learned the design of logical circuits, then static hazards may ring a bell. They are exactly the same phenomenon.

Reactive programming also assumes that the calculations are stateless. The individual nodes that perform the calculation may have a state in practice and, in most cases, they do. It is not inherently evil to have a state in some calculation. However, debugging something that has a state is significantly more complex than debugging something that is stateless, and functional.

It is also an important aid to the reactive environment, letting it perform different optimizations based on the fact that the calculations are functional. If the nodes have a state, then the calculations may not be rearranged freely because the outcome may depend on the actual evaluation order. These systems may not really be reactive, or, at least, this may be debated.

If this article piqued your interest in reactive programming and Java in general, you can explore Peter Verhas’s Java Projects – Second Edition to learn the fundamentals of Java 11 programming by building industry grade practical projects. Following a learn-as-you-do approach, Java Projects – Second Edition is perfect for anyone who wants to learn the Java programming language (no prior programming experience required).

Transfer Learning

Transfer learning does exactly as the name says. The idea is to transfer something learned from one task and apply it to another. Why? Practically speaking, training entire models from scratch every time is inefficient, and its success depends on many factors.

Another important reason is that for certain applications, the datasets that are publicly available are not big enough to train a deep architecture like AlexNet or ResNet without over-fitting, which means failing to generalize. Example applications could be online learning from a few examples given by the user or fine-grained classification, where the variation between the classes is minimal.

A very interesting observation is that final layers can be used to work on different tasks, given that you freeze all the rest, whether it is detection or classification, end up having weights that look very similar.

This leads to the idea of transfer learning. For example, ImageNet can generalize so well that it’s convolutional weights can act as feature extractors, similar to conventional visual representations and can be used to train a linear classifier for various tasks.

When?

Research has shown that feature extraction in convolutional network weights trained on ImageNet outperforms the conventional feature extraction methods such as SURF, Deformable Part Descriptors (DPDs), Histogram of Oriented Gradients (HOG), and bag of words (BoW).

This means we can used Convolutional features equally well with the conventional visual representations. The only drawback being that deeper architectures might require a longer time to extract the features.

That deep convolutional neural network is trained on ImageNet. The visualization of convolution filters in the first layers shows that they learn low-level features similar to edge detection filters. Whereas, the convolution filters at the last layers learn high-level features that capture the class-specific information.

Hence, if you extract the features for ImageNet after the first pooling layer and embed them into a 2D space. The visualization will show that there is some anarchy in the data. However, if we do the same at fully connected layers, we can notice that the data with the same semantic information gets organized into clusters. This implies that the network generalizes quite well at higher levels, and it will be possible to transfer this knowledge to unseen classes.

According to experiments transfer learning conducted on datasets with a small degree of similarity with respect to ImageNet, the features based on convolutional neural network weights trained on ImageNet perform better than the conventional feature extraction methods for the following tasks:

  • Object recognition: This CNN feature extractor can successfully perform classification tasks on other datasets with unseen classes.
  • Domain adaptation: This is when the training and testing data are from different distributions, while the labels and number of classes are the same. Different domains can consider images captured with different devices or in different settings and environment conditions. A linear classifier with CNN features successfully clusters images with the same semantic information across different domains, while SURF features overfit to domain-specific characteristics.
  • Finegrained classification: This is when we want to classify between the subcategories within the same high-level class. For example, we can categorize between bird species. CNN features, along with logistic regression, although not trained on fine-grained data, perform better than the baseline approaches.
  • Scene recognition: Here, we need to classify the scene of the entire image. A CNN feature extractor trained on object classification databases with a simple linear classifier on top, outperforms complex learning algorithms applied on traditional feature extractors on recognition data.

Some of the tasks mentioned here are not directly related to image classification, which was the primary goal while training on ImageNet and therefore someone would expect that the CNN features would fail to generalize to unseen scenarios. However, those features, combined with a simple linear classifier, outperform the hand-crafted features. This means that the learned weights of a CNN are reusable.

So when should we use transfer learning? When we have a task where the available dataset is small due to the nature of the problem (such as classify ants/bees). In this case, we can train our model on a larger dataset that contains similar semantic information and subsequently, retrain the last layer only (linear classifier) with the small dataset. 

If we have just enough data available, and there is a larger similar dataset to ours, pretraining on this similar dataset may result in a more robust model. As we normally train models with the weights randomly initialized, in this case, they will be initialized with the weights trained on this other dataset. This will facilitate the network to converge faster and generalise better. In this scenario, it would make sense to only fine-tune a few layers at the top end of the model.

How? An overview

There are two typical ways to go about this.

The first and more common way, is to use pre-trained model, a model that has previously been trained on a large scale dataset. Those models are readily available across different deep learning frameworks and are often referred to as “model zoos”.

The pre-trained model is largely dependent on what the current task to be solved is, and the size of the datasets. After the choice of model, we can use all of it or parts of it, as the initialized model for the actual task that we want to solve.

The other, less common way is to pretrain the model ourselves. This typically occurs when the available pretrained networks are not suitable to solve specific problems, and we have to design the network architecture ourselves.

Obviously, this requires more time and effort to design the model and prepare the dataset. In some cases, the dataset to pre-train the network on can even be synthetic, generated from computer graphic engines such as 3D studio Max or Unity, or other convolutional neural networks, such as GANs. The model pre-trained on virtual data can be fine-tuned on real data, and it can work equally well with a model trained solely on real data.

If we want to discriminate between cats and dogs, and we do not have enough data, we can download a network trained on ImageNet from the “model zoo”and use the weights from all but the last of its layers.

The last layer has to be adjusted to have the same size as the number of classes and the weights to be reinitialized and trained.

So it means we can freeze the layers that are not to be trained by setting the learning rate for these layers to zero, or to a very small number. In case a bigger dataset is available, we can train the last three fully connected layers. Sometimes, pre-trained network can be used only to initialize the weights and then be trained normally.

Transfer learning works because the features computed at the initial layers are more general and look similar. The features extracted in the top layers become more specific to the problem that we want to solve.

How? Code example

In this section you will learn the practical skills needed to perform transfer learning in TensorFlow. More specifically, we’ll learn how to select layers to be loaded from a checkpoint and also how to instruct your solver to optimize only specific layers while freezing the others.

TensorFlow useful elements

Transfer learning is about training a network initialized with weights taken from another trained model, we will need to find one. In our example, we will use the encoding part of a pretrained convolutional autoencoder. The advantage of using an autoencoder is that we do not need labelled data. It can be trained completely unsupervised.

An autoencoder without the decoder

An encoder (autoencoder without the decoder part) that consists of two convolutional layers and one fully connected layer is presented as follows. The parent autoencoder was trained on the MNIST dataset. Therefore, the network takes as input an image of size 28x28x1 and at latent space, encodes it to a 10-dimensional vector, one dimension for each class:

# Only half of the autoencoder changed for classification
class CAE_CNN_Encoder(object):
    ......
    def build_graph(self, img_size=28):
        self.__x = tf.placeholder(tf.float32, shape=[None, img_size * img_size], name='IMAGE_IN')
        self.__x_image = tf.reshape(self.__x, [-1, img_size, img_size, 1])
        self.__y_ = tf.placeholder("float", shape=[None, 10], name='Y')

        with tf.name_scope('ENCODER'):
            ##### ENCODER
            # CONV1: Input 28x28x1 after CONV 5x5 P:2 S:2 H_out: 1 + (28+4-5)/2 = 14, 
            # W_out= 1 + (28+4-5)/2 = 14
            self.__conv1_act = tf.layers.conv2d(inputs=self.__x_image, strides=(2, 2), name='conv1',
                              filters=16, kernel_size=[5, 5], padding="same", activation=tf.nn.relu)

            # CONV2: Input 14x14x16 after CONV 5x5 P:0 S:2 H_out: 1 + (14+4-5)/2 = 7,
            # W_out= 1 + (14+4-5)/2 = 7
            self.__conv2_act = tf.layers.conv2d(inputs=self.__conv1_act, strides=(2, 2),      
                name='conv2', filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu)

        with tf.name_scope('LATENT'):
            # Reshape: Input 7x7x32 after [7x7x32]
            self.__enc_out = tf.layers.flatten(self.__conv2_act, name='flatten_conv2')
            self.__dense = tf.layers.dense(inputs=self.__enc_out, units=200, activation=tf.nn.relu,                                                                                                                                                                                               name='fc1')
            self.__logits = tf.layers.dense(inputs=self.__dense, units=10, name='logits')

    def __init__(self, img_size=28):
        if CAE_CNN_Encoder.__instance is None:
            self.build_graph(img_size)

    @property
    def output(self):
        return self.__logits

    @property
    def labels(self):
        return self.__y_

    @property
    def input(self):
        return self.__x

    @property
    def image_in(self):
        return self.__x_image

Selecting layers

Once the model is defined, model=CAE_CNN_Encoder(), it is important to select layers that will be initialized with pretrained weights. Pay attention that the structure of both networks, must be the same. So, for example, the following snippet of code will select all layers with name convs of fc:

frommodelsimportCAE_CNN_Encodermodel=CAE_CNN_Encoder()

list_convs = [v for v in tf.global_variables() if "conv" in v.name]
list_fc_linear = [v for v in tf.global_variables() if "fc" in v.name or "output" in v.name]

Note that those lists are populated from tf.global_variables(); if we choose to print its content, we might observe that it holds all the model variables as shown:

[<tf.Variable 'conv1/kernel:0' shape=(5, 5, 1, 16) dtype=float32_ref>,
 <tf.Variable 'conv1/bias:0' shape=(16,) dtype=float32_ref>,
 <tf.Variable 'conv2/kernel:0' shape=(5, 5, 16, 32) dtype=float32_ref>,
 <tf.Variable 'conv2/bias:0' shape=(32,) dtype=float32_ref>,
 <tf.Variable 'fc1/kernel:0' shape=(1568, 200) dtype=float32_ref>,
 <tf.Variable 'fc1/bias:0' shape=(200,) dtype=float32_ref>,
 <tf.Variable 'logits/kernel:0' shape=(200, 10) dtype=float32_ref>,
 <tf.Variable 'logits/bias:0' shape=(10,) dtype=float32_ref>]

Once the layers of the defined graph are grouped into two lists, convolutional and fully connected, you will use tf.Train.Saver to load the weights that you prefer. First, we need to create a saver object, giving as input the list of variables that we want to load from a checkpoint as follows:

# Define the saver object to load only the conv variables
 saver_load_autoencoder = tf.train.Saver(var_list=list_convs)

In addition to saver_load_autoencoder we need to create another saver object that will allow us to store all the variables of the network to be trained into checkpoints.\

# Define saver object to save all the variables during trainingsaver=tf.train.Saver()

Then, after the graph is initialized with init=tf.global_variables_initializer() and a session is created, we can use saver_load_autoencoder to restore the convolutional layers from a checkpoint as follows:

# Restore only the weights (From AutoEncoder)
 saver_load_autoencoder.restore(sess, "../tmp/cae_cnn/model.ckpt-34")

Note that calling restore overrides the global_variables_initializer an all the selected weights are replaced by the ones from the checkpoint.

Training only some layers

Another important part of transfer learning is freezing the weights of the layers that we don’t want to train, while allowing some layers (typically the final ones).

In TensorFlow, we can pass to our solver only the layers that we want to optimize (in this example, only the FC layers):

train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss, var_list=list_fc_linear) 

Complete source

In this example, we will load the weights from a MNIST convolutional autoencoder example. We will restore the weights of the encoder part only and freeze the CONV layers. That train the FC layers to perform digits classification:

import tensorflow as tf 
import numpy as np 
import os 
from models import CAE_CNN_Encoder
SAVE_FOLDER='/tmp/cae_cnn_transfer' 
from tensorflow.examples.tutorials.mnist import input_data 
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)  
model = CAE_CNN_Encoder(latent_size = 20) 
model_in = model.input 
model_out = model.output 
labels_in = model.labels 

# Get all convs weightslist_convs=[vforvintf.global_variables()if"conv"inv.name]
# Get fc1 and logitslist_fc_layers=[vforvintf.global_variables()if"fc"inv.nameor"logits"inv.name]

# Define the saver object to load only the conv variablessaver_load_autoencoder=tf.train.Saver(var_list=list_convs)
# Define saver object to save all the variables during trainingsaver=tf.train.Saver()

# Define loss for classification
withtf.name_scope("LOSS"):loss=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=model_out,labels=labels_in))correct_prediction=tf.equal(tf.argmax(model_out,1),tf.argmax(labels_in,1))accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))# Solver configurationwithtf.name_scope("Solver"):train_step=tf.train.AdamOptimizer(1e-4).minimize(loss,var_list=list_fc_layers)

# Initialize variablesinit=tf.global_variables_initializer()# Avoid allocating the whole memorygpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.200)sess=tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))sess.run(init)

# Restore only the CONV weights (From AutoEncoder)saver_load_autoencoder.restore(sess,"/tmp/cae_cnn/model.ckpt-34")

# Add some tensors to observe on tensorboad
tf.summary.image("input_image",model.image_in,4)tf.summary.scalar("loss",loss)merged_summary=tf.summary.merge_all()writer=tf.summary.FileWriter(SAVE_FOLDER)writer.add_graph(sess.graph)

#####Train######
num_epoch=200batch_size=10forepochinrange(num_epoch):foriinrange(int(mnist.train.num_examples/batch_size)):# Get batch of 50 imagesbatch=mnist.train.next_batch(batch_size)# Dump summaryifi%5000==0:# Other summariess=sess.run(merged_summary,feed_dict={model_in:batch[0],labels_in:batch[1]})writer.add_summary(s,i)# Train actually here (Also get loss value)            _,val_loss,t_acc=sess.run((train_step,loss,accuracy),feed_dict={model_in:batch[0],labels_in:batch[1]})print('Epoch: %d/%d loss:%d'%(epoch,num_epoch,val_loss))print('Save model:',epoch)saver.save(sess,os.path.join(SAVE_FOLDER,"model.ckpt"),epoch)

If you enjoyed reading this article and want to learn more about convolutional neural networks. You can explore Hands-On Convolutional Neural Networks with TensorFlow. With an emphatic focus on practical implementation and real-world problems.

Hands-On Convolutional Neural Networks with TensorFlow is a must-read for software engineers and data scientists who want to use CNNs to solve problems.