Responsive Remainder Notes App

in #utopian-io7 years ago (edited)

vue.png
Source: http://vue-newsletter.com/vue-newsletter.png

In this opportunity, I invite you and explore my recent app as Single Page Application (SPA). But, before going any further, I will explain more detail how a component can be the most powerful in which client-side development of your app using VueJS. All of you have not deeply understand what is component related to VueJS's perspective, let you see at its official API and Guide. But, special for you don't waste more time, the most welcome to my article.

Murez Nasution


Attention!
All of experiments in this article has been tested and run well on the Windows platform. Other platforms never already tested, and some functionality would not run properly. Thank you.

For this moment, to be more easily understood, as always, I will use the method that would be the most effective, i.e. PnS – problem & solution.

Root Cause

Let's say that there is a simple note application template that consists of three main divisions, which are:

  1. Register, located at the most top of the document. It consists of two text inputs and a submit button, while pressed, creates a new note to be added down to Articles.
  2. Navigation, located on the left side of the document. Only contains labels that will display some information when selected. Next, it will be added some navigation buttons to update current selected article, as well as other additional needs
  3. Articles, located on the main part of the document. Contains all notes the user has created, which each note just consists of title and content.
    As seen below,

image.png

In the early steps, how do I make a note n times, as in the Articles division above? Maybe, you will finish it in a way that is not much different as I have done the following,

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
    </head>
<body>
    <div id="main"></div>
    <script>
    let init = function(n) {
        let canvas = window.main;
        for(let i = 0; i < n; i++) {
            canvas.insertAdjacentHTML(
                "beforeend",
                "<div>"
                    + "<h2>Title " + (i + 1) + "</h2>"
                    + "<p>Lorem ipsum dolor sit amet.</p>"
                + "</div>"
            );
        }
    }
    init(4);
    </script>
</body>
</html>

Save as index.html in any directory on your PC, and the result looks like the following,

image.png

The way above looks still effective if the context is still simple that will only display the title and content in each note. The real problem is how in each note it consists of more complex components (for example, there are additional images or list), structures that differ from other notes or update the content on current selected note. It takes a relatively longer time to create a good codes and memory management. Then, what's one of the best solutions? At this moment, VueJS will help us and save a lot of works.

Solution

It's time! But, before you can use VueJS, you must do some ceremonies first, as following:

Requirements

  1. Install NodeJS
  2. Install vue-cli
  3. Create New Project
  4. Rock ‘n Roll

For you who are familiar and have used Vue JS, can go directly to step (4). Make sure your PC is connected to the internet until the new project creation process is done.

1. Install NodeJS

You have to download NodeJS. Sure, we will not write any NodeJS codes, but just really need Node Package Manager (npm).

2. Install vue-cli

Open Command Prompt, then write npm install -g vue-cli. Wait for a moment.

3. Create New Project

Then, after vue-cli installation was completed, move to any directory as your target. Write vue init <template-name> <project-name>, commonly used:

vue init webpack my-project

Completely, you can find more current available templates in vuejs/vue-cli.

4. Install Text Editor

The following options deserve your attention:

5. Rock ‘n Roll

Here are some steps that will be done to be able implementing note application template that has been designed from the beginning as seen in the most top picture.

5.1. Articles Creation

Create a file as SimpleArticle.vue in the /src/components relative directory and type the following code,

<template>
    <div>
        <h1>{{ title }}</h1>
        <p>{{ content }}</p>
    </div>
</template>
<script>
export default {
    props: [ 'title', 'content' ]
}
</script>

image.png

Then, create another new file as Main.vue in the same relative directory as the previous file and write down the following code,

<template>
<div>
    <simple-article title='Title 1'
        content='Lorem ipsum dolor sit amet.'>
    </simple-article>
    <simple-article title='Title 2'
        content='Lorem ipsum dolor sit amet.'>
    </simple-article>
</div>
</template>
<script>
import SimpleArticle from '@/components/SimpleArticle'
export default {
    components: { SimpleArticle }
}
</script>
<style>
</style>

image.png

Make a minor change in /src/router/index.js, which finally looks like the following,

import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/components/Main'
Vue.use(Router)
export default new Router({
    routes: [
        {
            path: '/',
            name: 'Home',
            component: Main
        }
    ]
})

image.png

Save the changes and we'll see the results soon. But, make sure to do the following three steps:

  1. Open Command Prompt, then go to the project folder that already made before,
  2. Type npm run dev, press Enter. Wait for a moment, and
  3. Open browser, then access to localhost:8080/, it will look like the following,

image.png

Do you notice what's going on? In the previous scenario, it is seen that,

for(let i = 0; i < n; i++) {
    canvas.insertAdjacentHTML(
        "beforeend",
        "<div>"
            + "<h2>Title " + (i + 1) + "</h2>"
            + "<p>Lorem ipsum dolor sit amet.</p>"
        + "</div>"
    );
}

will iterate as much as n times and the result will be the same as,

<body>
    <div id="main">
        <div>
            <h2>Title 1</h2>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>
        <div>
            <h2>Title 2</h2>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>
        ...
    </div>
</body>

And Main.vue also transforms html tags almost similar like the following,

<div id="app">
    <div>
        <simple-article title='Title 1'
            content='Lorem ipsum dolor sit amet.'>
        </simple-article>
        <simple-article title='Title 2'
            content='Lorem ipsum dolor sit amet.'>
        </simple-article>
    </div>
</div>

Then, for each <simple-article> tag will be replaced in accordance with the existing template in SimpleArticle.vue, then become as follows,

<div id="app">
    <div>
        <div>
            <h2>Title 1</h2>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>
        <div>
            <h2>Title 2</h2>
            <p>Lorem ipsum dolor sit amet.</p>
        </div>
    </div>
</div>

So, if we add another new <simple-article> tag, then a new note will appear, and so on. The most important thing here is the way a parent (template that uses and declares the <simple-article> tag) sends data to its child (the SimpleArticle.vue component), i.e. through attributes.

We see that a child needs defined data in props, in this context is props: [ 'title', 'content' ].

Attention!
The value defined in the props should not be replaced, this is because the context of the data owned by the parent and only it has been responsible what happens to the props. Thus, a child should not interfere with the value in props (read-only). The app will still run well while still attempting to violate this rule. However, we will get a warning from VueJS to promptly correct this error in the Console. Instead, use the data properties to define variables in the local context (child itself).

Then parent provides and sends the data through the attribute of the <simple-article> tag, so simple that <simple-article title = '...' content = '...'>. Then, child can insert the data that has been sent in the html tag using the {{ ... }} syntax (next just known as interpolation), so <h1> {{ title }} </ h1> will display the text in the title property sent by its parent. And here's the component principle in VueJS perspective.

Looks the same and ordinary? OK. Let's go a step further. We will make Articles become more dynamic, by representing them as array. Next, add the following code to Main.vue,

<script>
import SimpleArticle from '@/components/SimpleArticle'
export default {
    data: function() {
        return {
            articles: [
                { title: 'Title 1', content: 'Lorem ipsum dolor sit amet.' },
                { title: 'Title 2', content: 'Lorem ipsum dolor sit amet.' }
            ]
        }
    },
    components: { SimpleArticle }
}
</script>

And just a bit modification on attribute in tag <simple-article>, so be

<simple-article v-for="article of articles"
    v-bind:title="article.title"
    v-bind:content="article.content">
</simple-article>

image.png

As same as previous one, but here we get some advantages, i.e.:

  • The v-bind: <any-prop> attribute is useful for making any property dynamic (next known as reactive), so that the values in the property can be changed fit to condition. This attribute must be populated with variables (also by using JavaScript expressions) declared in the data: function () {return { ... }}.

Attention!
The value in the data property must be a function that always gives new objects. Why should it be so? Wait for my next new article :-).

  • The v-for attribute is useful for doubling the tag associated with this attribute n times. In this context the <simple-article> tag will be created as many elements as in the data articles. The value in v-for can be the same JavaScript expression to iterate on the object.

5.2. Register Creation

Previously, we have finished implementing articles, then we will design register division. Go back to the Main.vue file, then add two inputs to receive the title and content, a submit button, as well as the method to be executed when the button is clicked. So the Main component becomes as follows,

<template>
<div>
    <input type='text' placeholder='Title' v-model='title' />
    <textarea placeholder='Content' v-model='content'></textarea>
    <button v-on:click='submit'>Submit</button>
    <simple-article v-for="article of articles"
        v-bind:title="article.title"
        v-bind:content="article.content">
    </simple-article>
</div>
</template>
<script>
import SimpleArticle from '@/components/SimpleArticle'
export default {
    data: function() {
        return {
            title: ‘’,
            content: ‘’,
            articles: [ ]
        }
    },
    methods: {
        submit: function() {
            if(Boolean(this.title) && Boolean(this.content)) {
                this.articles.unshift({ title: this.title, content: this.content })
                this.title = ''
                this.content = ''
            }
        }
    },
    components: { SimpleArticle }
}
</script>

image.png

Here, let the article blank. Next, focus to the browser and the results will be updated immediately (if no changes, press F5 or refresh button). For a while, the part that already exists in the document is only register divisions. Fill in the title and content, then hit submit. Thus, new notes will appear in the articles division, as shown below,

image.png

Cool! At this point, we have noticed the new attributes, i.e. v-model and v-on:<any-event>. We will discuss it more in depth in separate articles. For now, we just need to know that the v-on: click attribute will trigger the calling of the submit method when the button is pressed. All required methods on the component must be declared in properties methods. And in every method, it needs this that references to local variables.

5.3. Navigation Creation

This division is required to display an information when a note is selected. In this example, we will only show the date of the note when it was first created. The trigger is the click event on the title of the note. Already thought about how to do? OK. Let's get started!

In the Main.vue file, add more variable, e.g. created which will save the date of the note when it was first created. Then, we need an additional tag to display it. Remember! The trigger is the click event, on the title of the note. So, we also have to add a method, call it selected on the SimpleArticle component.

So, the addition to be done, among others, is on <template> in Main.vue,

<p> {{ created }} </ p>

Then, the addition to <script>,

data: function() {
    return {
        title: '',
        content: '',
        created: '',
        articles: [ ]
    }
}

Do not forget to add new attribute to existed object to be saved to Articles,

this.articles.unshift({ title: this.title, content: this.content, created: Date.now() })

And finally in the SimpleArticle.vue file, add the selected method and,

<h1 v-on:click='selected'>{{ title }}</h1>

Attention!
We have agreed not to change the value of props. Then, what should be we do in the selected method? How does child (SimpleArticle component) provide data back to its parent? The solution is event. So we need a method that will trigger an action in the parent context, it is $emit(eventname, value). For more details, the code in the SimpleArticle.vue file will be,

<template>
    <div>
        <h1 v-on:click='selected'>{{ title }}</h1>
        <pre>{{ content }}</pre>
    </div>
</template>
<script>
export default {
    props: [ 'title', 'content', 'created' ],
    methods: {
        selected() {
            this.$emit('selected:article', this.created)
        }
    }
}
</script>
<style scoped>
h1 { cursor: pointer }
</style>

image.png

And finally the Main.vue file will be as follows,

<template>
<div>
    <p>{{ created }}</p>
    <input type='text' placeholder='Title' v-model='title' />
    <textarea placeholder='Content' v-model='content'></textarea>
    <button v-on:click='submit'>Submit</button>
    <simple-article v-for="article of articles"
        v-bind:key='article.created'
        v-bind:title='article.title'
        v-bind:content='article.content'
        v-bind:created='article.created'
        v-on:selected:article='selected'>
    </simple-article>
</div>
</template>
<script>
import SimpleArticle from '@/components/SimpleArticle'
export default {
    data: function() {
        return {
            title: '',
            content: '',
            created: '',
            articles: [ ]
        }
    },
    methods: {
        submit: function() {
            if(Boolean(this.title) && Boolean(this.content)) {
                this.articles.unshift({ title: this.title, content: this.content, created: Date.now() })
                this.title = ''
                this.content = ''
            }
        },
        selected: function(v) {
            let d = new Date()
            d.setTime(v)
            this.created = 'Created Date: ' + (d.getMonth() + 1) + '-' + d.getDate() + '-' + d.getFullYear()
                + ' ' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds()
        }
    },
    components: { SimpleArticle }
}
</script>

image.png

The result will look like the following,

image.png

And finally, by applying some styles, our app will looks like this,

image.png

Conclusion

  • The way a parent to be able sending data to a child is through an attribute.
  • Child can receive data sent by its parent through props.
  • Components can only read props, while those who responsible to what is completely happening with props are just parent.
  • The scope of the child over its own data (local variables) is through the data property.
  • Property of data should always provide new objects in each instance. Then the data must be a function.
  • By giving the v-bind: prefix in an attribute, then the value of this attribute must be a variable or the JavaScript expression and props will be reactive. Which means that changing the value in parent, will also change the value in its child.
  • To do two-way interaction between parent and child (without having to change props) is with $emit(eventname, value).
  • VueJS is all about Reusable component.



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Your contribution cannot be approved yet because.

1 - Give proper Picture Courtesy for all the images used.
2 - You should not ask for follow, remove the line "Hopefully useful, and follow up my next article by click Follow here."

See the Utopian Rules. Please edit your contribution to reapply for approval.

You may edit your post here, as shown below:

You can contact us on Discord.
[utopian-moderator]

I've finished my editing related to your instructions. Thank you.

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Thank you.

Hey @murez-nst I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • This is your first accepted contribution here in Utopian. Welcome!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x