spice up your Golang app with a GUI — part 6: the Note component

video @ https://youtu.be/CwdYG8rTOdY

This tutorial would be broken into several stories; each of them exploring a separate concept so that you can read only the interesting pieces instead of everything. This is the 6th story in the series on creating the Note component and the related concepts. Links of the stories:

source code @ here:

https://github.com/quoeamaster/golang_blogs/tree/master/2020-03-gui-task-app

the Note

In the previous story / tutorial, we have created our dialog component and managed to retrieve the dialog’s input values. The next thing we would like to do is to create the Note according to the dialog’s input. So let’s create a note.js with the initial code:

Vue.component('note', {
template: `
<div class='note-container'>
to be detailed
</div>
`
});

It looks so simple and easy?! Yep it is~ Our Note UI is just an HTML div, and a css class “note-container” is applied here. From the app.css file; note-container is defined as follows:

.note-container {
position: absolute;
width: 200px;
height: 210px;
background-image: url("../memo.jpg");
background-size: cover;
}
.note-container:hover {
background-image: url("../memo_f_trans.png");
background-size: cover;
}

The div is backed by an image — memo.jpg which is the appearance of a sticky note, also the position setting is “absolute” as we would be setting the top and left attributes of a Note later on (e.g. if we did set the x coordinate to 40 and y coordinate to 100 on the create-note dialog; then we would need to set the Note’s top attribute to 100px and left attribute to 40px correspondingly). We also added a “hover” css class too; which means when our mouse pointer is over the div, this css class would be activated and overwrite the background-image setting — swapping to another background image with the bottom right edge tilted.

Now let’s update the index.html to include the note.js component:

...
<body onload="onStart()">
<div id="app">
<!-- splash -->
<splash-screen></splash-screen>
...
<!-- create note dlg -->
<create-note-dlg
v-on:on-display-dlg-update="onDisplayDlgUpdate"
v-bind:show="displayDlg"></create-note-dlg>

testing
<note></note>
</div>

<script src="./component/demo/note.js"></script>
<script src="./component/demo/splash.js"></script>
<script src="./component/demo/createNoteDlg.js"></script>
<script src="./component/demo/app.js"></script>

</body>
...

Re-build the app and run would yield:

Soft reminder on the build process (explanations available at Part 4 of the series):

go run generateAssets.go
mv generateAssets.go generateAssets.go.disable
go build -o taskApp
mv generateAssets.go.disable generateAssets.go
./taskApp

storing the Note definitions

An important concept here — the Dialog component is responsible for encapsulating the process on gathering the Note attributes AND send these attributes to the parent component, in our case the app component.

We have already done the 1st part, now in order to send back the Note attributes to the app component we can raise an event and let the app component react to it. A new function “onSave” is added as follows:

Vue.component('create-note-dlg', {
props: ['show'],
data: function() {
return {
x: "",
y: "",
content: ""
};
},
methods: {
...
onSave: function () {
this.$emit('on-add-note', {
x: this.x,
y: this.y,
content: this.content
});
this.$emit('on-display-dlg-update', false);
this.resetDialog();
},
resetDialog: function () {
this.x = '';
this.y = '';
this.content = '';
}

},
template: `
<div>
..
<div class="float-right">
<button class="btn btn-primary"
v-on:click="onSave">save</button>
<button class="btn btn-secondary"
v-on:click="onCancel">cancel</button>
</div>
...
</div>
`
});

An event is raised through the $emit function call, here we provide the payload of the event as well — the x, y coordinates and the content of the Note. The corresponding index.html would need to add back the event receiver setting:

<!-- create note dlg -->
<create-note-dlg v-on:on-display-dlg-update="onDisplayDlgUpdate"
v-on:on-add-note="onAddNote"
v-bind:show="displayDlg"></create-note-dlg>

A new function “onAddNote” would be added to the app.js:

new Vue({
el: '#app',
data: function () {
return {
displayDlg: false,
notes: []
};
},
methods: {
...
onAddNote: function (data) {
this.notes.push(data);
}

}
});

The first thing we added is a local state named as notes, which is a collection of Note attributes. The corresponding onAddNote function simply appends the payload (an object of the Note attributes) to the local state — notes.

updating the Note(s) position and contents

Next is to update the Note(s) based on the collected attributes. Let’s make some changes in the index.html:

<note v-for="note in notes"
v-bind:key="note.key"
v-bind:note="note"></note>

What is “v-for” — looping through the local state “notes” and each iteration would create exactly ONE note component. If we got 5 note information; then we would be creating 5 Note widget on the application’s main panel. For each iteration, the current note information would be referenced by the variable named “note”.

We also pass the “note” information to the note component through the props named “note” as well. Note that we also bind a “key” value to the component; in Vue.js there is a concept of re-using components, which means that if we are creating lots of Note component, there might be a chance that some older components might be re-used (in general, most mobile phone platforms also re-use UI components, for example the list items on Android or iOS). However, if we don’t want the components being re-used, we could assign a unique “key” value to each component.

The updated note.js code would be the following:

Vue.component('note', {
props: ['note'],
mounted: function () {
let noteObj = document.querySelector("#"+this.note.key);
if (noteObj) {
noteObj.style.top = this.note.y+'px';
noteObj.style.left = this.note.x+'px';
}

},
template: `
<div class='note-container' v-bind:id="this.note.key">
{{note.content}}
</div>
`
});

We first bind the “id” of the note with the provided key value (pass by the app component through the props named “note”). The mounted hook function was also added:

  • get a reference of the note HTML component — calling document.querySelector function
  • if the reference is valid; update the corresponding location of the Note through css manipulation

Again, no magic… The location of our Notes could be altered by changing css settings. You might ask why we access and manipulate css directly? Didn’t I told you that we should avoid directly changing HTML elements? Yep, the big rule is NOT to manipulate the HTML elements in an active way; however, for this particular scenario direct manipulation is the only way to achieve the goal… life is full of exceptions sometimes :)

PS. if anybody got a better suggestion on achieving the same goals but no need to access HTML elements’ attributes directly; please drop me a message or comment. Highly appreciated :D

Re-build the app and run would yield:

Done! We have accomplished a splendid job~

Moving on… by experience

In this story / tutorial, we have gone through the following:

  • create a Note by css settings on a HTML div — using background image(s) and the its hover css updates
  • make use of $emit to raise events with payload to its parent component — payload would be the Note information in our case
  • introduction on v-for syntax to iterate a list of items — each item would generate a Note component on the screen
  • the concept of component re-use and how to avoid it — set a unique “key” value on each component
  • by using css manipulation, we could change the Note’s position

It has been a long journey — from coding the backend (with the Lorca framework) to creating a simple web based application. Now the basic skeleton of the application is ready; but there are still a lot space for enhancements such as:

  • save the notes so that they could be retrievable and act as a “history” of tasks completed
  • for each note, should be able to rotate the angle as well — css manipulation with transform: rotate(XXXdeg)
  • add a search function for historical notes

I would leave all these explorations on your own; hopefully you would complete this application in the coming future~ If you would like to share the final application with me, please hesitate to drop me a message or comment here :))

PS. Writing a multi-story blog on a topic is something NEW to me; hence all types of suggestions are welcomed.

Finally, thanks for reading for so long~ Happy coding :)

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
devops terminal

devops terminal

114 Followers

a java / golang / flutter developer, a big data scientist, a father :)