{"id":13827985,"url":"https://github.com/aspittel/intro-to-vue","last_synced_at":"2025-07-09T05:31:05.445Z","repository":{"id":74189610,"uuid":"176793479","full_name":"aspittel/intro-to-vue","owner":"aspittel","description":null,"archived":false,"fork":false,"pushed_at":"2019-03-20T21:02:13.000Z","size":25,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-05T09:17:07.785Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aspittel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-03-20T18:24:20.000Z","updated_at":"2020-12-06T19:58:57.000Z","dependencies_parsed_at":"2024-01-18T05:06:10.220Z","dependency_job_id":"8cebf470-35a8-446b-a14f-eb29e9e94c19","html_url":"https://github.com/aspittel/intro-to-vue","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspittel%2Fintro-to-vue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspittel%2Fintro-to-vue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspittel%2Fintro-to-vue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspittel%2Fintro-to-vue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aspittel","download_url":"https://codeload.github.com/aspittel/intro-to-vue/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225486492,"owners_count":17481910,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T09:02:23.977Z","updated_at":"2024-11-20T07:31:22.122Z","avatar_url":"https://github.com/aspittel.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"# Vue Vixens DC's Hello World Meetup - Intro to Vue\n\n![Vue Vixens DC Logo](https://pbs.twimg.com/profile_images/1070775214370373633/borvu2Xx_400x400.jpg)\n\nVue.js is a frontend framework that is optimized for **progressive integration**. That means you can have a large app with only a couple Vue components integrated -- or you could start from scratch and work completely within the Vue ecosystem.\n\nAnother thing that sets Vue apart is the lower learning curve than a lot of frameworks. Instead of having to understand complex topics, if you know HTML, CSS, and JavaScript, you're already pretty close!\n\nLike any framework, it adds a structure and utilities to your frontend so that your app is easier to extend as it grows, is more organized, and you don't have to \"reinvent the wheel\" as often.\n\nVue is also really cool because it's ecosystem is really well integrated -- a lot of the utilities that would normally be 3rd party libraries are built by the Vue core maintainers, like [Vue Router](https://router.vuejs.org/) and [Vuex](https://vuex.vuejs.org/).\n\nThroughout this workshop, we'll explore the key features of Vue, and create an app together!\n\nHere's what we'll be building, though with some more interactive features. The like button will toggle from the heart outline to the red heart based on user clicks. Also, the character number will count down when someone types in the text box.\n\nfinished product - https://codepen.io/aspittel/pen/oVMBmO\n\nstarting app - https://codepen.io/aspittel/pen/oVMYqG\n\nGo ahead and check out the HTML and CSS code above, we'll be building off of the HTML with our Vue code.\n\n## Setting up a Vue App\n\nFor now, we'll use a Vue CDN -- we want a minimalist setup. In the future, you may want a more extensive environment, in which case you can use the [Vue CLI](https://cli.vuejs.org/).\n\nGo to the `settings` button on Codepen, switch to the JavaScript tab, and search for Vue on CDNjs. This adds the Vue library to our project, so we can use all of the methods and features that Vue gives us.\n\nNow, we need to create a Vue instance and attach it to our HTML in order to fully integrate Vue!\n\nLet's create a `const` that stores our `Vue` instance.\n\n```js\nconst app = new Vue()\n```\n\nWe're going to pass an object when we create this Vue app, it'll have all our configuration and application logic for now.\n\nThe first thing we're going to add to that object is `el` -- which is the element that we want to be the base of our Vue app. In this case the element with the `status` class.\n\n```js\nconst app = new Vue({\n  el: \".status\"\n})\n```\n\nThen, we'll add our `data`. To test this out, let's add the `tweetText` as data -- so where we have `Hello World!` right now will become a variable. Down the road we're going to make more tweets with different text, so it makes sense to make that piece of the tweet dynamic.\n\n```js\nconst app = new Vue({\n    el: \".status\",\n    data: {\n        tweetText: \"Hello World!\"\n    }\n})\n```\n\nWhen we want to add more dynamic data (or data that will change within our Vue app) we'll add more attributes to this `data` object.\n\nNow, we can use our newly created data in our HTML and plug in the variables that way! If you've ever used Handlebars or another templating language, it's kind of like that.\n\nIf you go to the hardcoded \"Hello World!\" in the HTML, we can now replace it with `{{tweetText}}` which will pull from our Vue data!\n\n```html\n\u003cp class=\"tweet-text\"\u003e\n  {{ tweetText }}\n\u003c/p\u003e\n```\n\nTry to change your `tweetText` in Vue, and it'll change in your output as well!\n\nLet's brainstorm for a second on what other data we have that will change within the course of our app.\n\n- The heart will toggle between liked and unliked\n- Our characters remaining will decrease when we type in the\n\n\u003c!--\nLet's go ahead and add attributes for those in our `data` object.\n\n```diff\ndata: {\n    tweetText: :\"Hello World!\",\n+    charactersRemaining: 280,\n+    liked: false\n}\n```\n\nWe'll also make `charactersRemaining` dynamic in the HTML.\n\n```html\n\u003cspan class=\"characters-remaining\"\u003e\n  {{ charactersRemaining }} characters remaining\n\u003c/span\u003e\n```\nWe'll hold off on the `liked` attribute for now, we'll come back to that in a second.\n--\u003e\n\n### Your turn: Add attributes for the `charactersRemaining` and `liked`. Add the `charactersRemaining` to the HTML too!\n\n## Methods\n\nNow that we have our data, we need to make it update based on user actions.\n\nWe're going to add another attribute to our Vue object -- this one will store our methods.\n\n```js\nconst app = new Vue({\n    el: \".status\",\n    data: {\n        tweetText: \"Hello World!\",\n        charactersRemaining: 280,\n        liked: false\n    },\n    methods: {}\n})\n```\n\nWe have two \"actions\" for our app -- toggling the like and changing the characters remaining number when the user types. Let's work on the character counting first.\n\nWe'll add a method to our methods object first:\n\n```js\nmethods: {\n    countCharacters: function() {\n\n    }\n}\n```\n\nLet's think about the logic for this function: we need to count how many characters the user has typed into the `textarea`. Then, we need to subtract that count from 280 (or our character limit).\n\nLet's create a data attribute for the comment text, and then update that every time the user types in the `textarea`.\n\n```diff\n  data: {\n    tweetText: 'Hello World!',\n    charactersRemaining: 280,\n+    commentText: '',\n    liked: false\n  },\n```\n\n```html\n\u003ctextarea placeholder=\"tweet your reply\" v-model=\"commentText\"\u003e\u003c/textarea\u003e\n```\n\n`v-model` is a _directive_ that syncs our data attribute with what the user has typed into the `textarea`. So no matter how much or little they have typed in, `commentText` will match what they've typed. To take one quick step back, _directives_ are HTML attributes that are provided by Vue, they're prefixed by `v-`.\n\nOkay, now back to our method. We can access our data in our methods with `this.myDataAttribute` ([here's](https://codeburst.io/all-about-this-and-new-keywords-in-javascript-38039f71780c) a great reference on JavaScript's `this`).\n\nSo, we can update the `charactersRemaining` with the following logic:\n\n```js\nmethods: {\n    countCharacters: function() {\n        this.charactersRemaining = 280 - this.commentText.length\n    }\n}\n```\n\nNow, we need to make sure that `countCharacters` runs every time the user types in the `textarea`.\n\nLuckily, Vue has the `v-on` directive, and we can add the event after it so that we run the method each time that event takes place. In this case, `v-on:input=\"countCharacters` will run the `countCharacters` method each time the user types in the `textarea`.\n\n```html\n\u003ctextarea\n  placeholder=\"tweet your reply\"\n  v-model=\"commentText\"\n  v-on:input=\"countCharacters\"\n\u003e\u003c/textarea\u003e\n```\n\nOkay, now let's step back and work on our `toggleLike` method.\n\n### Your turn: Create a `toggleLike` method!\n\n\u003e Hint: instead of running this event on input, we want to run it on click!\n\nDon't worry about changing emojis quite yet, we'll go over conditionals together in a minute!\n\n\u003c!--\nWe first need to add the method to our `methods` object.\n\n```js\nmethods: {\n    ...\n    toggleLike: function () {\n\n    }\n}\n```\n\nThe body of the method should change `this.liked` to the opposite of what it currently is. So:\n\n```js\ntoggleLike: function () {\n    this.liked = !this.liked\n}\n```\n\nOkay, now we need to make that action run.\n\nOn our `reactions` div, let's add an event listener.\n\n```html\n\u003cdiv class=\"reactions like\" v-on:click=\"toggleLike\"\u003e\n  ...\n\u003c/div\u003e\n```\nNow, it's time to introduce another Vue feature: conditionals!\n--\u003e\n\n\n## Conditionals\n\nVue allows us to conditionally render data with the `v-if` directive.\n\nLet's add the following span-wrapped emoji within our `reactions` div:\n\n```html\n\u003cspan v-if=\"liked\"\u003e♥️\u003c/span\u003e\n```\n\nNow, our red heart emoji only shows up if `liked` is `true`. Let's also add a `v-else` to our heart outline emoji, so that it only renders if `liked` is `false`.\n\n```html\n\u003cspan v-if=\"liked\"\u003e♥️\u003c/span\u003e \u003cspan v-else\u003e♡\u003c/span\u003e\n```\n\nYay! Now our likes work!\n\nIf you had any issues with the above steps, here's a Codepen with what we have so far.\n\nhttps://codepen.io/aspittel/pen/WmKRNbG\n\nNow that we have our interaction down, how would we create a bunch more tweets with the same functionality but different state and text? Components!\n\n## Components\n\nSimilar to other frontend frameworks, Vue apps are broken down into components. We compose components together in order to create full user interfaces. A good rule of thumb is that if a chunk of the user interface is used multiple times, it should be broken into a component.\n\nIn a production application, our tweet would probably be broken into subcomponents -- we may have a component for the comment text area, one for the like functionality, one for the profile picture, etc. But, for tonight, we will just make the full tweet into a component so that we can easily create a bunch more tweets.\n\nFirst, let's move the logic from our Vue instance into a component.\n\nThe first argument to `Vue.component` is the name of the component, in this case \"tweet\". We're also turning data into a function that returns an object. This allows us to have multiple `tweet` component instance, each with separate data.\n\n```js\nVue.component(\"tweet\", {\n  data: function() {\n    return {\n      charactersRemaining: 280,\n      commentText: \"\",\n      liked: false\n    }\n  },\n  methods: {\n    countCharacters: function() {\n      this.charactersRemaining = 280 - this.commentText.length\n    },\n    toggleLike: function() {\n      this.liked = !this.liked\n    }\n  }\n})\n```\n\nWe also need the `template` for the component -- or the HTML that the component will render. We're going to grab all of the existing HTML and paste into a template attribute on our component.\n\n```js\ntemplate: `\u003cdiv class=\"status\"\u003e\n  \u003cdiv class=\"tweet-content\"\u003e\n    \u003cimg src=\"https://pbs.twimg.com/profile_images/1070775214370373633/borvu2Xx_400x400.jpg\" class=\"logo\" alt=\"Vue Vixens DC logo\"\u003e\n    \u003cdiv class=\"tweet\"\u003e\n      \u003ca href=\"https://twitter.com/vuevixensdc\"\u003eVue Vixens DC\u003c/a\u003e\n      \u003cspan\u003e@VueVixensDC · Mar 20\u003c/span\u003e\n      \u003cp class=\"tweet-text\"\u003e\n        {{ tweetText }}\n      \u003c/p\u003e\n      \u003cdiv class=\"reactions\"\u003e\n        \u003cspan v-on:click=\"toggleLike\" class=\"like\"\u003e\n          \u003cspan v-if=\"liked\"\u003e♥️\u003c/span\u003e\n          \u003cspan v-else\u003e♡\u003c/span\u003e\n        \u003c/span\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv class=\"comment-bar\"\u003e\n    \u003ctextarea placeholder=\"tweet your reply\" v-model=\"commentText\" v-on:input=\"countCharacters\"\u003e\n    \u003c/textarea\u003e\n    \u003cspan class=\"characters-remaining\"\u003e\n      {{ charactersRemaining }} characters remaining\n    \u003c/span\u003e\n  \u003c/div\u003e\n\u003c/div\u003e`\n```\n\nNow, we have a Vue component!\n\nOne other quick thing we need to add: the tweet text is going to be different from tweet to tweet. We'll pass in different tweet text for each individual tweet through `props` -- which allow us to pass data to a component from outside of that component. For now, we'll just specify that our component has a prop associated with it.\n\n```js\nVue.component('tweet', {\n  props: ['tweetText'],\n...\n})\n```\n\nWe still have to have a Vue app though, so let's add that back into our JavaScript:\n\n```js\nnew Vue({ el: \"#app\" })\n```\n\nCool, now our JavaScript is set, we just have to handle our HTML. In our Vue instance, we're looking for an element with the id `app` now, so let's create that.\n\n```html\n\u003cdiv id=\"app\"\u003e\u003c/div\u003e\n```\n\nAnd, inside of our new Vue app, we'll add some instances of our tweet component.\n\n```html\n\u003cdiv id=\"app\"\u003e\n  \u003ctweet tweet-text=\"hello world!\"\u003e\u003c/tweet\u003e\n  \u003ctweet tweet-text=\"hi!\"\u003e\u003c/tweet\u003e\n\u003c/div\u003e\n```\n\nNotice how we're passing in our `tweetText` prop -- Vue converts the JavaScript camel case to kebab case in HTML. Outside of that change, our props look like HTML attributes.\n\nNow our component should be good to go!\n\nhttps://codepen.io/aspittel/pen/YgjNjb\n\nOne more quick thing though, usually instead of hardcoding each tweet in the HTML, we're going to want to loop through a data structure and create a tweet component for each of those items. Let's look at how to do that in Vue!\n\nWe're going to go into our Vue app instance and add some tweet data.\n\n```js\nnew Vue({\n  el: \"#app\",\n  data: {\n    tweets: [\n        { id: 1, tweetText: \"hello world!\" }, \n        { id: 2, tweetText: \"hi!\" }\n    ]\n  }\n})\n```\n\nNow we'll use another Vue directive, `v-for` in order to loop through the tweets array and create a `tweet` instance for each!\n\n```html\n\u003cdiv id=\"app\"\u003e\n  \u003ctweet\n    v-for=\"tweet in tweets\"\n    v-bind:key=\"tweet.id\"\n    v-bind:tweet-text=\"tweet.tweetText\"\n  \u003e\u003c/tweet\u003e\n\u003c/div\u003e\n```\n\nNotice that we use `v-bind` twice here -- it allows us to dynamically update html attributes (or use variables within them). Keys are recommended whenever you use `v-for` -- it allows Vue to identify the child elements better ([more](https://vuejs.org/v2/guide/list.html#key)).\n\nAwesome! Now we can create more tweets by adding an element to the `tweets` array!\n\nHere's all of that code together.\n\nhttps://codepen.io/aspittel/pen/oVMBmO\n\n## Next Steps\n\nFirst, there's a lot of cool features that you can add to the widget we just built. You can make the profile pictures different from tweet to tweet, along with the date and user data. You can also disable or highlight overflow text in our textarea. You could even use the Twitter API to use real tweets and even make the comment posting work!\n\nHere are some more awesome resources for continuing to learn Vue:\n\n* [Vue Vixens on DEV](https://dev.to/vuevixens)\n* [Sarah Drasner's Vue series](https://css-tricks.com/intro-to-vue-1-rendering-directives-events/)\n* [The Vue Documentation](https://vuejs.org/)\n* [This workshop in blog post format!](https://dev.to/aspittel/a-complete-beginners-guide-to-vue-422n)\n\n## Keep in touch!\n\n* https://www.meetup.com/VueVixens-DC/\n* https://twitter.com/ASpittel\n* https://dev.to/aspittel\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faspittel%2Fintro-to-vue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faspittel%2Fintro-to-vue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faspittel%2Fintro-to-vue/lists"}