{"id":18409,"date":"2017-08-30T12:16:55","date_gmt":"2017-08-30T09:16:55","guid":{"rendered":"https:\/\/www.webcodegeeks.com\/?p=18409"},"modified":"2017-08-30T12:10:55","modified_gmt":"2017-08-30T09:10:55","slug":"building-simple-notes-manager-vue-js","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/","title":{"rendered":"Building a Simple Notes Manager with Vue.js"},"content":{"rendered":"<p>Vue.js fascinates me. The author is <a href=\"https:\/\/twitter.com\/youyuxi\">Evan You<\/a>, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and build a simple, easy to learn, and extremely productive framework.<\/p>\n<p>To show you the benefits of using Vue.js, I thought I\u2019d make something useful from the ground up. I decided to go for apps like SimpleNote; clean and straightforward apps for taking notes. And the core part seems to be small enough to implement in a day. Then I decided to be even more down-to-earth and build a Chrome extension that will replace the new tab page with the notes app.<\/p>\n<p>So in this article, you are going to learn:<\/p>\n<ul>\n<li>How to set up a Vue.js project from the ground up (not hard at all)<\/li>\n<li>How to create components and make them interact<\/li>\n<li>A little bit of theory, so you know how it works underneath<\/li>\n<li>How to create a Google Chrome extension<\/li>\n<li>How to store data in <code>chrome.storage<\/code><\/li>\n<\/ul>\n<h2>Setting Up the Scene<\/h2>\n<p>Okay, let\u2019s get started. We are going to use <a href=\"https:\/\/yarnpkg.com\/\"><code>yarn<\/code><\/a> for dependency management.<\/p>\n<pre class=\"brush:bash\"># create a directory and cd into it\r\nmkdir notes &amp;&amp; cd $_\r\n\r\n# create a new package.json with defaulted answers\r\nyarn init --yes<\/pre>\n<p>All right, that\u2019s a start. Now we need to set up webpack and babel because we want to use ES6 features and make sure they are properly transpiled to ES5, which is better understood by major browsers.<\/p>\n<p>We will also need a special <code>vue-loader<\/code> that deals with Vue\u2019s <a href=\"https:\/\/vuejs.org\/v2\/guide\/single-file-components.html\">single file components<\/a> (more on it later). We will also install a bunch of other dependencies, but don\u2019t worry. I\u2019ll explain why we need each one.<\/p>\n<pre class=\"brush:bash\"># Let's install webpack and babel\r\nyarn add webpack babel-core babel-loader babel-preset-es2015 --dev\r\n\r\n# Install vue-loader which works for .vue files\r\nyarn add vue-loader --dev\r\n\r\n# Plugin that creates an index.html file for us, so we don't need to care\r\nyarn add html-webpack-plugin --dev\r\n\r\n# We want to use pug instead of plain html\r\nyarn add pug vue-template-compiler --dev\r\n\r\n# Process style files\r\nyarn add style-loader css-loader --dev\r\n\r\n# File loader will help us to copy manifest.json\r\nyarn add file-loader --dev\r\n\r\n# Finally this is our production dependency\r\nyarn add vue<\/pre>\n<p>We will also need the <code>webpack.config.js<\/code>. You can find it <a href=\"https:\/\/github.com\/hiquest\/zenotes\/blob\/master\/webpack.config.js\">here<\/a>. Download it and place in the root of your project. The essential part of every webpack config is the rules section, in which you define how different files should be processed.<\/p>\n<pre class=\" brush:javascript\">\/\/ ...\r\n    rules: [\r\n      \/\/ Vue-loader is needed to process .vue files.\r\n      \/\/ We then tell it that any js code should be transpiled with babel.\r\n      {\r\n        test: \/\\.vue$\/,\r\n        loader: 'vue-loader',\r\n        options: {\r\n          loaders: {\r\n            js: 'babel-loader?presets[]=es2015'\r\n          }\r\n        }\r\n      },\r\n      \/\/ Any other JS files should be also transpiled\r\n      {\r\n        test: \/\\.js$\/,\r\n        exclude: [\/node_modules\/],\r\n        use: [{\r\n          loader: 'babel-loader',\r\n          options: { presets: ['es2015'] },\r\n        }],\r\n      },\r\n      \/\/ Simply copy .json and .png files from src to dist\r\n      {\r\n        test: \/\\.(json|png)$\/,\r\n        use: 'file-loader?name=[name].[ext]'\r\n      },\r\n      \/\/ Compile and copy any css files\r\n      {\r\n        test: \/\\.css$\/,\r\n        use: [ 'style-loader', 'css-loader' ]\r\n      }\r\n    ]\r\n\/\/ ...<\/pre>\n<p>You can now try to run <code>yarn start<\/code>. It should start up webpack and start complaining that there\u2019s no <code>src\/index.js<\/code> file. We\u2019ll fix this later. But first, let\u2019s take a closer look at what we are going to deal with.<\/p>\n<h2>Vue.js Overview<\/h2>\n<p>As a long-time Angular 1 developer, I find Vue.js fascinating. We all know these Angular drawbacks: steep learning curve, confusing dependency injection system (which gets even worse with ES6 modules), overcomplicated directives\/components, and a huge set of docs.<\/p>\n<p>Vue.js managed to keep the Angular productivity spirit, but keep things simple. It is much easier to learn, component-oriented, and faster, too, because it\u2019s not using the dirty-checking thing. Instead, it employs the virtual DOM, much like React does. So it\u2019s productive from Day One.<\/p>\n<p>Vue.js encourages the use of components from the start. Here\u2019s how a typical component might look:<\/p>\n<pre class=\"brush:javascript\">Vue.component('my-component', {\r\n  template: '&lt;span&gt;{{ message }}&lt;\/span&gt;',\r\n  data: {\r\n    message: 'hello'\r\n  }\r\n})\r\n\r\nnew Vue({\r\n  el: '#example'\r\n})<\/pre>\n<p>Let\u2019s pause there and talk a little bit. As I said earlier, Vue.js kind of takes all the good things from Angular and React. It means that it still supports the two-way binding, and every Angular developer will find it very familiar. But the way it works underneath is not the same.<\/p>\n<p>There\u2019s no dirty-checking. So how does Vue track the changes and know when to update the view? Well, it utilizes <a href=\"http:\/\/raganwald.com\/2015\/08\/24\/ready-get-set-go.html\">getters and setters<\/a>. When you pass a data object to Vue, it walks through its properties and converts them to getter\/setters using <code>Object.defineProperty<\/code>. Getters and setters are ES5-only, which is why Vue doesn\u2019t support IE8 and below (well, who cares, right?).<\/p>\n<p>Another caveat that follows from that is that you always need to specify properties that you will use in a data object, even though they are undefined at the start.<\/p>\n<h2>Building a Chrome Extension<\/h2>\n<p>Chrome extensions are easy. They just regular HTML + JavaScript web pages with access to some Chrome APIs. We\u2019ll need a <a href=\"https:\/\/github.com\/hiquest\/zenotes\/blob\/master\/src\/manifest.json\">manifest file<\/a> to glue it all together. Create <code>src\/manifest.json<\/code> and add to it:<\/p>\n<pre class=\"brush:javascript\">{\r\n  &quot;manifest_version&quot;: 2,\r\n  &quot;name&quot;: &quot;Zenotes&quot;,\r\n  &quot;description&quot;: &quot;Replace the new tab page with clean, distraction-free notes&quot;,\r\n  &quot;version&quot;: &quot;1.0&quot;,\r\n  &quot;chrome_url_overrides&quot; : {\r\n    &quot;newtab&quot;: &quot;index.html&quot;\r\n  },\r\n  &quot;permissions&quot;: [\r\n    &quot;activeTab&quot;,\r\n    &quot;storage&quot;\r\n  ]\r\n}<\/pre>\n<p><code>chrome_url_overrides<\/code> tells the browser that we want to replace the new tab page with our content. And then we also add <code>permissions<\/code> to get access to APIs.<\/p>\n<p>Now let\u2019s also create a simple Vue app that does nothing much. Let\u2019s create our first component:<\/p>\n<pre class=\"brush:javascript\">\/\/ src\/js\/components\/App.vue\r\n&lt;template&gt;\r\n  &lt;h1&gt;Hello World&lt;\/h1&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script&gt;\r\nexport default {\r\n  \/\/ we'll add stuff later\r\n  data: function() {\r\n    return {};\r\n  }\r\n}\r\n&lt;\/script&gt;\r\n\r\n&lt;style&gt;\r\n  h1 { font-size: 22px; }\r\n&lt;\/style&gt;<\/pre>\n<p>Note that a component is described in a file with extension <code>.vue<\/code> and it has markup (HTML), JavaScript code and CSS stylings all in one file. In Vue terminology, this is called <a href=\"https:\/\/vuejs.org\/v2\/guide\/single-file-components.html\">Single File Components<\/a>. While this approach is not without its flaws, it makes a lot of sense to me, since all those are essential parts of a component and they are very coupled together. So it\u2019s only logical to keep them in one file.<\/p>\n<p>On the dark side of things, this file can get very large, and your editor (Vim in my case) can blow up or start working veeeeeery slowly.<\/p>\n<p>In order for webpack to be able to process single file components, we need to include <code>vue-loader<\/code> (see the previous section).<\/p>\n<p>One last piece that we need is to give Vue control over our page.<\/p>\n<pre class=\" brush:javascript\">\/\/ src\/js\/index.js\r\n\r\n\/\/ loading manifest (webpack will simply copy it into .\/dist)\r\nrequire(&quot;..\/manifest.json&quot;);\r\n\r\nimport Vue from 'vue';\r\nimport App from '.\/components\/App.vue';\r\n\r\nnew Vue({\r\n  el: 'body',\r\n  render: h =&gt; h(App)\r\n});<\/pre>\n<p>Time to see if it works. Run in a command line:<\/p>\n<pre class=\"brush:bash\">yarn start<\/pre>\n<p>Now open Chrome. Go to <code>chrome:\/\/extensions\/<\/code>. Make sure that Developer Mode is turned on. And then <em>Load unpacked extension<\/em>. Specify the <code>dist<\/code> directory. Now your extension is loaded, and you should see \u201cHello World\u201d when you open a new tab.<\/p>\n<h2>Displaying the List of Notes<\/h2>\n<p>All right, that\u2019s a great start. Let\u2019s move on and make something interesting.<\/p>\n<p>If you never saw an app like SimpleNotes, it\u2019s very intuitive. You have a sidebar on the left with the list of all your notes, and then the central area is an editor. You can switch the current note by clicking on it in the sidebar, and then edit it in the editor. Our editor will be able to use markdown. But let\u2019s start with a sidebar.<\/p>\n<pre class=\" brush:javascript\">\/\/ src\/components\/App.vue\r\nbody\r\n  .notes.row\r\n    .column.column-20.sidebar\r\n      h5\r\n        a(href='#' @click=&quot;addNote&quot;) +\r\n      ul\r\n        li(v-for=&quot;note in notes&quot; :key=&quot;note.id&quot; :class='{ active: note === selected }' @click=&quot;selectNote(note)&quot;) {{ note.body }}\r\n    .column.column-80(style='position: relative')\r\n      textarea(v-if='selected' v-model=&quot;selected.body&quot; placeholder=&quot;Edit me&quot;)<\/pre>\n<p>Oh well. A lot of stuff going on here. Let\u2019s got through it line by line.<\/p>\n<p>Note that we\u2019re using Milligram here to split our screen into two parts. Classes like <code>row<\/code>, <code>column<\/code>, <code>column-20<\/code>, etc. are all part of Milligram\u2019s grid API which allows us to quickly markup our grid. Of course we need to install it first:<\/p>\n<pre class=\"brush:bash\">yarn add milligram<\/pre>\n<p>Now, to Vue-specific things:<\/p>\n<ul>\n<li>\n<code>@click<\/code> directive is a shortcut for <code>v-on:click<\/code>. It binds the DOM click event to a method in your component (we\u2019ll get to that later). We use it twice in this snippet: first, to add a new note, and second, to set the active one.\n<\/li>\n<li>\n<code>v-for<\/code> iterates over an array of things, in this case, notes. We also provide a <code>:key<\/code> directive for it to be able to differentiate between the notes.\n<\/li>\n<li>\n<code>{{ note.body }}<\/code> is a common one-way binding syntax. Again it\u2019s very similar to Angular 1.\n<\/li>\n<li>\nFinally, <code>v-model<\/code> creates two-way data bindings on a form input and textarea elements.\n<\/li>\n<\/ul>\n<p>Now all these should be very intuitive to Angular 1 users. I guess that\u2019s why I got so easily hooked in the first place.<\/p>\n<p>Let\u2019s define our component:<\/p>\n<pre class=\"brush:javascript\">\/\/ src\/components\/App.vue\r\n&lt;script&gt;\r\n\/\/ requiring milligram framework styles\r\nrequire(&quot;milligram&quot;);\r\n\r\nexport default {\r\n  data: () =&gt; {\r\n    return {\r\n      notes: [],\r\n      selected: undefined\r\n    };\r\n  },\r\n  methods: { addNote, selectNote }\r\n};\r\n\r\n\r\nfunction selectNote(note) {\r\n  if (note === this.selected) return;\r\n  this.selected = note;\r\n}\r\n\r\nfunction addNote() {\r\n  const note = { id: guid(), body: '# ' };\r\n  this.notes.unshift(note);\r\n  this.selectNote(note);\r\n}\r\n\r\n\/\/ generate unique IDs\r\nfunction guid () {\r\n  return s4() + s4() + '-' + s4() + '-' + s4() + '-' +\r\n    s4() + '-' + s4() + s4() + s4();\r\n}\r\n\r\nfunction s4() {\r\n  return Math.floor((1 + Math.random()) * 0x10000)\r\n    .toString(16)\r\n    .substring(1);\r\n}\r\n&lt;\/script&gt;<\/pre>\n<p>Here we define our data object and our methods. Note that in methods we can refer to <code>this<\/code>, which contains both data and methods. So if we want to add a new note, we do <code>this.notes.unshift({ id: guid(), body: '# ' });<\/code>. Changes are immediately displayed in the view.<\/p>\n<p>You can try it yourself. Reload the browser and click on \u201c+\u201d to add a note.<\/p>\n<h2>Storing Data<\/h2>\n<p>We can add notes. That\u2019s nice. But how can we save stuff so that when you open a new tab, all your notes are still there? <a href=\"https:\/\/developer.chrome.com\/extensions\/storage\">Chrome storage<\/a> to the rescue!<\/p>\n<p>Chrome storage is very similar to <code>localStorage<\/code> with several differences. The most important is that we can use <code>storage.sync<\/code> API and have all our data synchronized across every browser user have. Let\u2019s add a <code>save<\/code> function that\u2019s going to save all our notes into storage.<\/p>\n<pre class=\"brush:javascript\">const SKEY = &quot;ZENOTES&quot;;\r\n\r\nfunction addNote() {\r\n  const note = { id: guid(), body: '# ' };\r\n  this.notes.unshift(note);\r\n  this.selectNote(note);\r\n  save(this.notes);\r\n}\r\n\r\nfunction save(notes) {\r\n  if (!notes) return;\r\n  chrome.storage.sync.set({[SKEY]: notes});\r\n}<\/pre>\n<p>Note, that we use \u201cZENOTES\u201d as our global key for storing notes array.<\/p>\n<p>We also should load notes when the component is bound. It is easy to do with the components life-cycle hooks.<\/p>\n<pre class=\"brush:javascript\">export default = {\r\n  data: () =&gt; {\r\n    return {\r\n      notes: [],\r\n      selected: undefined\r\n    };\r\n  },\r\n  methods: { addNote, selectNote },\r\n  mounted: function() {\r\n    const vm = this;\r\n    loadNotes(this);\r\n  }\r\n}\r\n\r\nfunction loadNotes(vm) {\r\n  chrome.storage.sync.get(SKEY, ({ [SKEY]: list = [] }) =&gt; {\r\n    \/\/ Push all notes to our array with ES6 fancy splat syntax\r\n    vm.notes.push(...list);\r\n    \/\/ Select the most recent note\r\n    vm.selectNote(list[0]);\r\n  });\r\n}<\/pre>\n<p>Just in case this <code>({ [SKEY]: list = [] })<\/code> drives you crazy, this is a new ES6 destructuring syntax. It says that whatever we get, we expect it to be an object and take the value by the key <code>SKEY<\/code> and put it into <code>list<\/code> variable. And if it\u2019s not there, set an empty array. I know it might not seem readable the first time, but it\u2019s a very expressive language syntax that saves several lines of code.<\/p>\n<p>Reload your page. Now you should be able to add notes and they will be there every time you reload or open a new tab. But if you edit an existing note, these changes won\u2019t be saved. This is because we need to set up a watcher.<\/p>\n<p><a href=\"https:\/\/vuejs.org\/v2\/guide\/computed.html#Watchers\">Vue watchers<\/a> provide a way to track changes in data and react to it somehow. In our case, we want to save notes every time the body of a selected note is changed. Here\u2019s how we do it.<\/p>\n<pre class=\"brush:javascript\">\/\/ ...\r\nexport default {\r\n  \/\/ ...\r\n  mounted: function() {\r\n    \/\/ ...\r\n    this.$watch(\r\n      watchable.bind(this),\r\n      onChange.bind(this),\r\n      { deep: true }\r\n    );\r\n  }\r\n};\r\n\r\nfunction watchable() {\r\n  if (!this.selected) return undefined;\r\n  return { id: this.selected.id, body: this.selected.body };\r\n}\r\n\r\nfunction onChange(val, prev) {\r\n  if (!prev) return;\r\n  save(this.notes);\r\n}<\/pre>\n<p>Update the page. Now you can type anything and it will be immediately stored in Chrome. Open a new tab, reload the browser, and it will still be there.<\/p>\n<h2>Markdown Editor<\/h2>\n<p>I wanted to be able to edit my notes in markdown just because I like it so much. But also because it is such a perfect way to demonstrate how can we integrate a third-party JavaScript library into Vue ecosystem. We will use <a href=\"https:\/\/simplemde.com\/\">simplemde<\/a>, so install it with yarn.<\/p>\n<pre class=\"brush:bash\">yarn add simplemde<\/pre>\n<p>Then guess what? We\u2019re going to make <a href=\"https:\/\/github.com\/hiquest\/zenotes\/blob\/master\/src\/js\/components\/Editor.vue\">a component<\/a>! Here\u2019s how it will be used:<\/p>\n<pre class=\"brush:javascript\">editor(v-if='selected' v-model='selected.body' :key=&quot;selected.id&quot;)<\/pre>\n<p>(Replace our regular textarea with this snippet)<\/p>\n<pre class=\"brush:javascript\">\/\/ src\/js\/components\/Editor.vue\r\n  &lt;template lang='pug'&gt;\r\n    .note-area\r\n      textarea\r\n  &lt;\/template&gt;\r\n\r\n  &lt;script&gt;\r\n  \/\/ 3rd party markdown editor\r\n  import SimpleMDE from 'simplemde';\r\n  \/\/ and don't forget the styles\r\n  require(&quot;simplemde\/dist\/simplemde.min.css&quot;);\r\n\r\n  \/\/ Exporting our component\r\n  export default {\r\n    props: ['value'],\r\n    mounted\r\n  };\r\n\r\n  function mounted() {\r\n    \/\/ Init the editor\r\n    const md = initEditor(this.$el.childNodes[0], this.value);\r\n\r\n    \/\/ Focusing into and setting cursor at the end (100 is long enought)\r\n    md.codemirror.focus();\r\n    md.codemirror.setCursor(100);\r\n\r\n    \/\/ On change communicate to v-model\r\n    md.codemirror.on(&quot;change&quot;, () =&gt; {\r\n      const val = this.md.value();\r\n      this.$emit('input', val);\r\n    });\r\n\r\n    this.md = md;\r\n  }\r\n\r\n  function initEditor(el, val) {\r\n    return new SimpleMDE({\r\n      element: el,\r\n      initialValue: val,\r\n      spellChecker: false,\r\n      placeholder: &quot;Type here...&quot;,\r\n      toolbar: false,\r\n      toolbarTips: false,\r\n      status: false,\r\n      autoDownloadFontAwesome: false,\r\n      forceSync: true\r\n    });\r\n  }\r\n  &lt;\/script&gt;<\/pre>\n<p>So what\u2019s important here? This part:<\/p>\n<pre class=\"brush:javascript\">props: ['value']<\/pre>\n<p>Value is a property that is going to be mounted by <code>v-model<\/code> so it\u2019s accessible via <code>this.value<\/code>.<\/p>\n<p>And then this part.<\/p>\n<pre class=\"brush:javascript\">md.codemirror.on(&quot;change&quot;, () =&gt; {\r\n    const val = this.md.value();\r\n    this.$emit('input', val);\r\n  });<\/pre>\n<p>Every time the content of the editor changes, we are signaling that the model is changed with <code>$emit<\/code> function. These two attributes are a kind of interface for <code>v-model<\/code>. So every time you want your component to support <code>v-model<\/code>, you need both of these things. They are the key.<\/p>\n<p>We also need to register this new component. I prefer to do it in the <code>index.js<\/code> file:<\/p>\n<pre class=\" brush:javascript\">\/\/ ...\r\nimport Editor from '.\/components\/Editor.vue';\r\nVue.component('editor', Editor);\r\n\/\/ ...<\/pre>\n<p>This is all we need. So now we have a fully functional notes manager, which works perfectly well in a browser. But there\u2019s still one thing that I want to talk about.<\/p>\n<h2>Transitions<\/h2>\n<p>We\u2019ve come to the understanding that proper transitions and animations are an essential part of any decent website. Vue gets it perfectly right and the <a href=\"https:\/\/vuejs.org\/v2\/guide\/transitions.html\">documentation<\/a> talks in detail about specifics and best practices. Let\u2019s see how we can make the selected note appear and slide down from the top. Turns out this is very easy.<\/p>\n<p>Vue provides a component called <code>transition<\/code>. So all we need to do is to wrap our element with it.<\/p>\n<pre class=\"brush:javascript\">transition(name=&quot;fade&quot; appear)\r\n  editor(v-if='selected' v-model='selected.body' :key=&quot;selected.id&quot;)<\/pre>\n<p>Then add those styles to <code>App.vue<\/code> component<\/p>\n<pre class=\"brush:css\">&lt;style&gt;\r\n.note-area {\r\n  padding-left: 20px;\r\n  position: absolute;\r\n  width: 100%;\r\n}\r\n\r\n.fade-enter-active, .fade-leave-active {\r\n  transition: opacity .2s ease-in, transform .2s ease-in;\r\n}\r\n\r\n.fade-enter, .fade-leave-to {\r\n  opacity: 0;\r\n  transform: translateY(-30px);\r\n}\r\n&lt;\/style&gt;<\/pre>\n<p>That\u2019s it. Try to update the page now and switch to different notes.<\/p>\n<h2>Wrapping It All Up<\/h2>\n<p>So finally we\u2019ve created our Chrome extension so that we can add notes right into our new tab page. We also learned a few things along the way:<\/p>\n<ul>\n<li>We know how to create a simple Chrome extension now and utilize internal API like <code>chrome.storage<\/code>.<\/li>\n<li>We set up a development environment for a Vue project even without the Vue CLI (you still can give it a try).<\/li>\n<li>It\u2019s clear how Vue handles changes, is Reactive, and doesn\u2019t do dirty-checking.<\/li>\n<li>We know how to create components and make them interact with each other.<\/li>\n<li>We can wrap third-party libraries in Vue-ready components with the help of v-model.<\/li>\n<li>Finally, we learned how easy it is to implement CSS transactions with Vue.<\/li>\n<\/ul>\n<p>I hope that by this time you are starting to feel that extraordinary power that Vue.js puts in your hands.     <\/p>\n<p>This is just a start.<\/p>\n<div class=\"attribution\">\n<table>\n<tr>\n<td><span class=\"reference\">Reference: <\/span><\/td>\n<td><a href=\"https:\/\/blog.codeship.com\/building-a-simple-notes-manager-with-vue-js\/\">Building a Simple Notes Manager with Vue.js<\/a> from our <a href=\"http:\/\/www.webcodegeeks.com\/join-us\/wcg\/\">WCG partner<\/a> Yanis Triandaphilov at the <a href=\"http:\/\/blog.codeship.com\/\">Codeship Blog<\/a> blog.<\/td>\n<\/tr>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Vue.js fascinates me. The author is Evan You, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and build a simple, easy to learn, and extremely productive framework. To show you the benefits of using Vue.js, I thought I\u2019d make something useful from the &hellip;<\/p>\n","protected":false},"author":227,"featured_media":909,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[25],"tags":[427],"class_list":["post-18409","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-angular-js","tag-vue-js"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Building a Simple Notes Manager with Vue.js - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"Vue.js fascinates me. The author is Evan You, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building a Simple Notes Manager with Vue.js - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"Vue.js fascinates me. The author is Evan You, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2017-08-30T09:16:55+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Yanis Triandaphilov\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Yanis Triandaphilov\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/\"},\"author\":{\"name\":\"Yanis Triandaphilov\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/556c1e47406608dd65cbdbc1217575c1\"},\"headline\":\"Building a Simple Notes Manager with Vue.js\",\"datePublished\":\"2017-08-30T09:16:55+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/\"},\"wordCount\":1966,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg\",\"keywords\":[\"Vue.js\"],\"articleSection\":[\"Angular.js\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/\",\"name\":\"Building a Simple Notes Manager with Vue.js - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg\",\"datePublished\":\"2017-08-30T09:16:55+00:00\",\"description\":\"Vue.js fascinates me. The author is Evan You, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"JavaScript\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/javascript\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Angular.js\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/javascript\/angular-js\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Building a Simple Notes Manager with Vue.js\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/556c1e47406608dd65cbdbc1217575c1\",\"name\":\"Yanis Triandaphilov\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7b336d9c2bb6f83299994032828d8717bae760e864c4ddd75f1b02e61ceed860?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/7b336d9c2bb6f83299994032828d8717bae760e864c4ddd75f1b02e61ceed860?s=96&d=mm&r=g\",\"caption\":\"Yanis Triandaphilov\"},\"description\":\"Yanis Triandaphilov lives and works in Prague. He's been into Ruby and JavaScript development for more than six years now. \\\"Being not too smart, I struggle for clarity and simplicity in everything I do.\\\"\",\"url\":\"https:\/\/www.webcodegeeks.com\/author\/yanis-triandaphilov\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Building a Simple Notes Manager with Vue.js - Web Code Geeks - 2026","description":"Vue.js fascinates me. The author is Evan You, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/","og_locale":"en_US","og_type":"article","og_title":"Building a Simple Notes Manager with Vue.js - Web Code Geeks - 2026","og_description":"Vue.js fascinates me. The author is Evan You, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and","og_url":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_published_time":"2017-08-30T09:16:55+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg","type":"image\/jpeg"}],"author":"Yanis Triandaphilov","twitter_card":"summary_large_image","twitter_creator":"@webcodegeeks","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Yanis Triandaphilov","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/"},"author":{"name":"Yanis Triandaphilov","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/556c1e47406608dd65cbdbc1217575c1"},"headline":"Building a Simple Notes Manager with Vue.js","datePublished":"2017-08-30T09:16:55+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/"},"wordCount":1966,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg","keywords":["Vue.js"],"articleSection":["Angular.js"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/","url":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/","name":"Building a Simple Notes Manager with Vue.js - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg","datePublished":"2017-08-30T09:16:55+00:00","description":"Vue.js fascinates me. The author is Evan You, a former Angular developer. Somehow he managed to take the best of the two worlds \u2014 Angular and React \u2014 and","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/angularjs-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/javascript\/angular-js\/building-simple-notes-manager-vue-js\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"JavaScript","item":"https:\/\/www.webcodegeeks.com\/category\/javascript\/"},{"@type":"ListItem","position":3,"name":"Angular.js","item":"https:\/\/www.webcodegeeks.com\/category\/javascript\/angular-js\/"},{"@type":"ListItem","position":4,"name":"Building a Simple Notes Manager with Vue.js"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/556c1e47406608dd65cbdbc1217575c1","name":"Yanis Triandaphilov","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/7b336d9c2bb6f83299994032828d8717bae760e864c4ddd75f1b02e61ceed860?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/7b336d9c2bb6f83299994032828d8717bae760e864c4ddd75f1b02e61ceed860?s=96&d=mm&r=g","caption":"Yanis Triandaphilov"},"description":"Yanis Triandaphilov lives and works in Prague. He's been into Ruby and JavaScript development for more than six years now. \"Being not too smart, I struggle for clarity and simplicity in everything I do.\"","url":"https:\/\/www.webcodegeeks.com\/author\/yanis-triandaphilov\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/18409","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/227"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=18409"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/18409\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/909"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=18409"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=18409"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=18409"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}