Minesweeper: A playful intro to Vue.js | Hunor Borbély | Skillshare

Playback Speed

  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

Minesweeper: A playful intro to Vue.js

teacher avatar Hunor Borbély, Web developer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

18 Lessons (59m)
    • 1. Introduction

    • 2. Creating and running a project

    • 3. Tooling

    • 4. Going through the initial project

    • 5. Breaking down the app into components

    • 6. The tiles

    • 7. The minefield

    • 8. Data

    • 9. Utility functions

    • 10. Resetting the game

    • 11. Surrounding bombs

    • 12. Computed properties

    • 13. Flagging a tile

    • 14. Revealing a tile

    • 15. Did we win already?

    • 16. Every second counts

    • 17. Review & Next steps

    • 18. Bonus: Share your game

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.





About This Class

Do you want to learn Vue the fun way? In this class, you will learn the key concepts of Vue while building a Minesweeper game.

While building a fully functioning game we dive into some basic concepts in Vue like:

  • How to break down your application or game into smaller pieces, into components
  • How to structure your logic and how to connect these components with props and custom events
  • How to deal with change and make your game data-driven
  • How to add interaction and game logic
  • And how to publish the final result to the web to share it with your friends

This is an intro course, no prior Vue knowledge is required, but some HTML, CSS, and basic Javascript skills are necessary. You should also know how to navigate in your terminal.

Meet Your Teacher

Teacher Profile Image

Hunor Borbély

Web developer


Since I was a kid I loved building things. First just played with Lego and now I'm creating websites. I find it really fascinating how easy it is to make amazing things on the web with just an ordinary computer once you dive a bit deeper into web development. Yet getting there is overwhelming, there's too many content out there. To get over the early difficulties I'll help you out with explaining complex topics in a simple and practical way.

See full profile

Class Ratings

Expectations Met?
  • Exceeded!
  • Yes
  • Somewhat
  • Not really
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.


1. Introduction: Hi there, I'm Hunor, and in this course we are going to learn how to create a Minesweeper game while learning Vue.js. If you haven't used Vue.js before, don't worry, we are going to cover all the basics of Vue.js while building this game. There's a lot to cover though, this course is going to be dense. We start from scratch by creating a new project and setting up the environment. And we finished by publishing our game on the web. All you need to have is some prior HTML, CSS, and JavaScript knowledge. And you should be able to navigate in your terminal. And by the end of this course, you will know how to create a simple game or website with Vue.js 2. Creating and running a project: In this course, we are going to build a Minesweeper game from scratch. And along the way, I'm going to explain the fundamentals of Vue.JS. So let's jump straight into it and create a project. For that, we are going to use a command-line tool that comes from the Vue.Js team. This tool will help us to generate the initial files, the foundation of our project, and provide some scripts that are able to create a live preview of the project during development, and the final bundle that we can publish on the internet. In order to get and use this tool, we need to set up node first if you don't already have it. If you don't already have node on your computer, go to nodejs.org and install the latest stable version marked as LTS. Once we did that you can check if it works by going to the terminal and typing node -v If you see a version number, node is working. Once we have node, we can install the command line tool for Vue in the terminal type npm install -g @vue/cli This might take a while, but once it's done, we can create our project. Navigate to a folder where you want to have your project. I usually have all my projects in the project folder and run 'vue create minesweeper'. It asked if you want to go with the default settings or you want to change that. But in this course, we just go with the default. Once again, this takes a while, it sets up a lot of things. Now let's check out how we can see our project. If you open the project, you can see it comes with a README file that says for development, we can compile and hotreload the project with npm run serve. This means that While we run the script. It will generate a live preview for our project. That's will refresh automatically when we change any of the thighs. So let's move to the terminal again. Make sure that we are in the folder where our project is and run 'npm run serve'. This gives us a URL where we can't see the live preview. And voila, there it is. If npm serve gives you an error. Like, no file or directory and something about package.json that means you are in the wrong folder. Makes sure you navigate where your project is. This script will continuously run in the background until you stop it, exit the terminal or shut down your computer. You can always stop it with Control-C or Command-C, depending on your OS. When you stop the script, you get back to the terminal and then you can run the strip the same way again. During this whole course, I'm going to run the script in the background to generate a live preview for us. So make sure you are also running this script to see what you are doing. If your browser gets out of sync, which can happen sometimes, then try to refresh the browser and that should fix it. If uppon refreshing the browser says this side can be reached, death means the script is not running. So go to the terminal and navigate to the folder where your project is and run 'npm run serve'. 3. Tooling: As a code editor in this course, I'm going to use Visual Studio Code, which is nowadays the de facto standard for web development even on Macs while it's coming from Microsoft. For every development, I recommend using the prettier and ES lint extensions. Let me show what they do. I opened the project we generated previously. And before getting into the details, let me just change this a bit. Now, this is technically the same code as before, but it's a bit harder to read. When we save the file though, it jumps back to its previous nicer shape. This is about prettier does, formats the code based on best practices. We have the formatting doesn't happen automatically. Go to Settings, look up the format of the save option, and make sure that it's checked. You can also override some settings, like if you'd prefer single quotes instead of double quotes. Or you don't want to have semicolons at the end of your lines, you can configure that, and prettier will take care of it. Let me make another change. This is also valid JavaScript, but it gets highlighted in red. This is ESlint's doing. If you haver over your mouse, it gives a brief message about the problem. And if you still need to know more about it, you can click this link and read more. White is better, is considered harmful. And how could you fix it? If you don't agree just like with prettier, ESlint can also be configured. For this project because we are using Vue, I also recommend using the Vetur extension that comes with Vue specific code formatting auto completion and error checking. 4. Going through the initial project: Let's check out the example project that we just created because we can learn a lot from it already. There's a main.js file. This is the starting point and it's also the only place where we are going to see 'new Vue'. This bit defines that our root component is app, which is coming from an app.vue file. Not a .js file, a .vue file. Vue files refer to single file components. They are called single file components because they include both the HTML, JavaScript, and CSS part of a component. Once we open app.vue, we can see these parts. The template is basically the output of the component piece of HTML that is going to be part of the website. It starts the usual way. It hasn't div, then it has an image. But then it gets interesting. 'Hello world' is not an HTML tag. If a tag's name appears to be an upper camel case then it refers to a subcomponent. Vue is a component-based library and it has a tree structure of components. Let me show what that means. Let's imagine that this is a huge HTML file with hundreds of lines and it became hard to understand. On a closer look, we can notice that it causes of distinct parts. In Vue. We can move these parts into separate components. And then suddenly our original HTML that now we can call root component, becomes much shorter because it delegated all the details of the menu and the content, the subcomponents, and now it only refers to them. Of course, the result of this composition in the browser, will be the same long HTML, but during development, we can work with smaller, more manageable bits. Back to code. What we are looking at is the root component and hello world is our first subcomponent. How do we know that this file is the root? We know this because this was referred in main.js. Otherwise, these looks the same as any other component. The sample app is a very simple one. It has one root component and one subcomponent. But the root component could have many more subcomponents. And those subcomponents could have their own subcomponents and so on. Now let's check out where the subcomponent hello world is coming from. For that, we need to go to the script which can be interpreted as a companion for our template. Every component script has a default export object that can define the component's name, its behavior, data, and subcomponents. This object can be a bit intimidating at first, but it's just an object with key-value pairs. Some of the values are represented by a string, sum by an object, some by a function, and some by an object that contain functions. Sometimes we also use shorthands in this object, like in the components option, don't get misguided here, this is a shorthand of a key-value pair. This means the same as this, where the key is referring to the HelloWorld in the template, the value refers to the import that points to another .vue file. If you go to that file, we see a similar structure. There's a template with a bit more tags. There's a script and the style. But before checking this, let's go back for a second and note something else here. When we have this unusual HTML tag, HelloWorld, we also have an attribute here. This is a way of passing on a message or a variable to a subcomponent. If you think of components as functions, which they are not. But just for the analogy, if you imagine them as functions, then their template could be interpreted as their output. and what you can see here is an input. In the code, if you go back to the HelloWorld component, we see the script that has no subcomponents. It has prompts. This is where we define that this component requires a message property that should be a string. Then if you dig deeper in the template, we see a reference to this variable between double curly brackets. These double curly brackets are called Mustache template, and they basically open a window from HTML to JavaScript. Here we have a JavaScript variable, but we could also have any other JavaScript expression here. The last thing we should note before creating our own components is the style. By default, CSS is global. It doesn't matter in which file you define a rule. It affects every HTML tag that it fits. This can lead to a huge global CSS file that is hard to maintain. Instead of that with single file components, we can define scoped style. If we add the scoped attribute to the style tag then Vue will make sure that this style is only affecting this component. This way we'd single file components we not only encapsulate layout, logic, and style, but we can also make sure that the style we define does not interfere with other components. 5. Breaking down the app into components: Once we saw how the initial application looks like, let's try to break down our Minesweeper game into components. As we saw, there's always a root component, that is the frame of the whole app. The game we are going to build is not too big nor complex. So technically we could fit the whole app into this one component. But if you tried to fit everything in one file, that soon becomes long and hard to understand. In general, if a file is longer than a 100 or a 150 lines you might want to break it down into smaller bits. There are two ways to create new components. One is to avoid repetition. If the sampling appears multiple times or at multiple locations on a website, we create a new component for it. The do this, even if they're not entirely the same, but the changes could be defined by some simple parameters. In our case, we are creating a component for the tiles. They are not the same, some hide the bomb, some contain a number, but they still roughly look the same and they all do something once you click them. The other method of breaking down an app into components is to find smaller meaningful bits. Maybe we found a piece that is not repeating but it has it's own behaviour that is not relevant to any other part of the game or application. By breaking down app visa repetition is usually rather easy. Breaking down an app based on logic or style is less obvious and there are multiple good solutions. In our case, we're going to create a separate component for the timer. Because as we are going to see, it held its own unique logic. We could also create a component for the header section of the app to make our root component simpler, but in this course, we are not going to do that. We are only going to have a root component that takes care of the main layout and the logic. We'll have a tile component that is responsible for how the tiles look. And we're going to have a timer that counts the seconds once the game has started. 6. The tiles: So let's start building our game. In this first step, we are creating the Tile component that will show a number and add a few tiles to the board. So let's create a new file called Tile.vue in the components folder. Then we add a template for it. This template for now will only contain a div with the class tile. And as a content, I just add the number one here. This is already a valid component, even without the script or the style tag. But if you don't see it in the website yet because it's not used. So let's save this file and go back to the app.vue file. And let's import it the same way as the HelloWorld component is imported. Then we add it to the components. And now we can use it in the template. Now if we save this file then we can already see our basic type components in the browser. We want to make them look more like ties though. So let's go back to the Tile.vue file and define the style for the tag class. We want our ties to look like squares so let's set the width and the height of 50 pixels. Also, let's add the border to make them more visible. And finally, I'm using flexbox to center the content. So now we have three tiles, but they all have the same. Let's make them unique. We are going to pass on an index number to them the same way as the HelloWorld component receives a message. First, let's go to the Tile components, add the script part and define that it will have a prop called index, which should be a number. Then we can use this index prop in our template between double curly brackets. Now, this component is able to show a number. We only have to pass it on from app. Let's go back to app component and pass on a number for these tiles. Now, this looks all good and the numbers are showing up in the browser as you can see, that this is actually not correct. If you go to the consol in the the browser, we will see why. It says expected number with value three, got string with the value three. Seems like the same thing, but it's actually different. We've passed on three as a text. When passing on props, there are three ways of doing it. We can pass down like a normal HTML attribute, like what we just did here. And then the value we pass on will behave like a string. Or we can prefix the attribute name with a colon. And then the value will behave like a JavaScript expression. Now we have a number here which will act as a number but we could also have a JavaScript expression here or reference to value. This is going to be important later on when we pass some variables instead of hardcoded values. We already saw a way of using JavaScript expressions in our template. That was with double curly brackets. In a way, we are doing the same thing here, with a different syntax. They are not interchangeable though. Double curly brackets are used in the content of an HTML tag. Prefixing a key with a colon though works when you are using JavaScript as a value of an attribute. So basically the same thing, but different context. Now we've fixed the issue, but the error is still there in the console of the browser. This is because previous messages on the console don't disappear automatically. But if you refresh the whole page, we see that it's not there anymore. Generally, a keep the browser console open during development because Vue gives a lot of useful messages. But I wanted to fit this recording in a smaller screen, so I'm closing it now. Before moving on, let's clean up things that we won't need from the original code. We're not going to need the image tag in the app's template, so let's delete it along with the asset folder. Also lets delete the HelloWorld component with any reference to it from the template and the script tag. And let's get rid of the style tag as well, because we are going to create our own style. Okay not that we have a clean look in the next chapter, we are going to move from three times to the wall grid. 7. The minefield: In the previous lesson, we managed to create a component and pass on a prop to its instances. But our minesweeper game is going to consist of more than three tiles. Instead of manually adding more and more ties, we are going to generate the tiles with a loop. In Vue, you can enrich the template with directives. One of them is the v-for directive. The v-for directive can repeat an HTML tag or a component. It looks like an HTML attribute. You write v-for="index in 100". This will generate a 100 tiles for us. It also creates a new index variable that we can use in other attributes of this tag or within that tag. In order to make the loop work for technical reasons, we also need to provide a unique key attribute for each of the tile instances. As a value, we can simply pass on the new index variable because that is going to be unique. But remember to add the column prefix to the key attribute. Because otherwise the value of the key will be literarily the text index instead of a number which it represents. To have a visual representation of the index of the loop. Let's pass on our new variable to the index prop of the component. Now we see a bunch of ties having numbers from one to 100. But it doesn't exactly look like a grid. So let's add some layout. First, let's remove the other two ties and move this time with a loop inside of a new div that will have the class board. Then define the class of this board. We're going to use CSS grid to have the layout we want. Also let's add some style to the app to make sure our content is centered and not stretched out. And now we have a layout. Before moving on, I'd like to point out an important bit here while we see the index values of the tiles. Our data model now, and along the course will be 1-dimensional. This means that our tiles don't have a separate row index and column index, but they have one index. And only CSS makes it look like two-dimensional. This is going to be a bit tricky when we have to calculate the bomb surrounding the tile. But we worry about that when we get there. 8. Data: We managed to generate the amount of tiles we need. As a next step, let's make a data-driven. Let's switch from showing the index of the tile to show whether the tile contains a bomb or not. For this, we define a data option in the component's script. The data option is practically an object with a list of properties that we can refer to in the template. But for technical reasons, instead of defining it simply as an object, it has to be defined as a function that returns an object. That's why if you define it as an object then on save ESlint will automatically replace it with a function. Here we are going to define the tiles property. The tiles property will be the metadata of our app, therefore, it will be an array representing the tiles. For simplicity, let's just add a bunch of zeros and ones indicating if the tie should contain a bomb or not. The properties in the data option can be used in the template. So it can be also used in our v-for loop. We can replace the number 100 with the tiles property and see what happens. We see a representation of our tiles array in the browser, but the behavior of this loop is slightly different now. Previously it was generating a range from one to 100. Therefore calling its variable index was appropriate. But now it's representing the item itself, which can be either 0 now or 1. Which leads us to a problem, the loops variable isn't unique anymore. If you check your browser's console, it says we are using the key 0 and the key 1 multiple times. So let's clean up things a bit. First, let's rename the index to tile. Both in the loop and in the tile component. Secondly, we still need to use the unique key for our ties. Luckily, the v-for directive can also provide an index, we just need a slightly different syntax for it. So now we can pass on this index to the key, and we pass on the tile variable as the prop for the component. Everything is set. 9. Utility functions: Let's create a utility file that generates our tiles. This is going to be a separate file outside of our components. Why is it going to be separate? Because we can separate it. It's a good practice to keep our files relatively short. Components already have their own template, script and style. So if you can separate a piece of the logic that makes sense on its own, then that's usually a good thing. Not everybody can be moved outside though, some interact with the component itself. But if a function has no side effects or dependency on its surrounding, then we can move it to a utility file. So let's create a file called utils.js. And let's explore a function called generateTiles from it. We also going to need the constant defining how many bombs do we have on the field. Let's call this totalNumberOfBombs and set it to 14. Inside the generateTiles function, we first generate an empty array that has a length of 100 items. Then write a loop that keeps on going till it planted as many bombs in the array as the total number of bombs constant indicates. In each cycle, we generate a random tile index. And if the selected tile does not already have a bomb then we plant a bomb there. Once the array is ready we return it. And now we're going to import it in the app. In App.vue, we can import this function in the script part. Then we can assign it to the tiles property in the data option. Now every time we refresh the page, we see another layout of bombs. But how could we get a new layout without refreshing the browser? 10. Resetting the game: It's time to add some interactivity to our game. As a first step, we create the reset button. The Reset button is the smiley face located into header. So first we need to create the header. Let's go to app.vue and add the header above the board. This will contain the number of remaining bombs, which for now is a hard-coded number 14. The reset button itself, with the smiley, and a timer. The timer will be later a separate component, but for now it's just a hard-coded number 0. After adding these bits, let's fix the style by adding a new div that will contain both the header and the Board. Then let's add some style, let's align the items in the header with CSS flexbox. Add some margin, and increase the font size to make it look more like a header. Unfortunately, the button doesn't follow the Increase font-size automatically. So let's change that as well. Now we get to the script part. We need to change the value of tiles in the data option. For this, we can create a method for our view component. The methods option is a collection of functions that can change the data and can be called from our template. So let's create a methods option in our default export. Its value is going to be an object. And let's add the function called reset to it. Then we write this.tiles = generateTiles(). Before linking this method to the button, we have to talk about the this keyword. The this keyword is one of the most complicated things in JavaScript, and I'm not even trying to explain what it actually is. To give you a simplified explanation. You can think of them in the context of Vue development as the component itself. In the default export of the script, we define component data, props, methods and as later we are going to see computed properties. If you want to access or change any of that inside the script, we need the this keyword. The this keyword only works in the context of default export though. Within JavaScript component data, perhaps methods and computed properties can only be accessed within the default export. The generateTiles function does not need any of that. That's why we could move it outside of the component. It's just a function that returns something that will be assigned to a data. But the assignment itself where we need the this keyword is within the default export. Another thing you might notice is when we use data, preps or methods in the template then we do not use the this keyword. We can refer to them directly. Why is that? Well, not everything is consistent. Vue is a library on top of other technologies and it has to follow their rules. So as a rule of thumb, if you refer to data, props or computed properties in the script, then we need to use the this keyword. If we refer to the same things within the template that we don't. Now that said all that, let's link this method to the button. In the template we can define an event handler like an attribute starting with the @ key. There are several events we can choose from, but we just use the most simple one and the click event. In our template after the bottom, we can write @click="reset". And that's it. Now every time you click the smiley face, it generates a new minefield. 11. Surrounding bombs: Let's updates our data model and include the number of surrounding bombs for each tile. For this, we need to update our generateTiles function and return an object for each tile that will encapsulate both the bomb indicator, and the number of surrounding bombs. This chapter won't do anything Vue-specific things. We are going to adjust the game logic and I am not going to lie, it will involve some mathematics. The difficulty is finding the neighboring tiles. Van we map our bombs array for each tile, we only have the value indicating if the tile contains a bomb or not, the index of the tile, the whole original array. The problem is that despite appearances, are data model is still a one-dimensional array. So the index is a number between 0 and the number of items in the array minus one. That is 24 here, but in our game we are developing that's 99. How do we know, for instance, that the neighbors of tile six are the tile numbers 0 1 2 5 7 10 11 and 12. First, to figure out the coordinates for tile six. We create a function that gives back in which row and which column the tile is located. You can get the row by dividing the index six with the number of items in the row, which here is five, in our real application, it's ten. That gives us 1.2, but the row number has to be a whole number so we use Math.floor. Calculating the column is similar, but instead of dividing by the number of items in a row, we use the modulo operator that gives the remainder of a division. In this case one. We ended up with coordinate 1,1, which can be a bit misleading because tile six is in the second row and in the second column bits we are using zero-based indexes here. So remember the first row is actually row 0. With this function, we can calculate the row and the column for each of our tile indexes. Now we can easily figure out the coordinates of the surrounding tides, but we still need to figure out their indexes. We create another function that takes a row and the column and gives back an index. This is an easier calculation. We simply multiply the row number with the number of items in a row and we add the column number, fit this function, we can finally calculate the surrounding bombs for each tile. We define a variable that will contain, the number of surrounding bombs, then go through each neighbor and check if it contains a bomb. We first got the top left index, then check if the original array had a bomb indicator at that position. Then we get the top index and check if the original array had a bomb above the tile, and so on. At the end of the map we return the object that contains the bomb indicator and the number of surrounding bombs for each tile. We ended up with a data model we originally wanted. But this calculation has some edge cases. What if we want to calculate the neighbors of tile 14, the neighbors should be tile 8 9 13 18 and 19. But this calculation adds some extra tiles to the mix. When we tried to calculate the top-right style, we will get the coordinate row one, column five, which is outside of our grid. But their logic does not handle that. It's simply looks up its index that will be one times five plus five, ending up at tile ten, which is not a neighbor of tile 14. So we have to handle us some edge cases here. The easiest way to solve this is to return undefined when we are looking up an index for a coordinate that is outside of our grid. So in our getIndex function, if a column or row is below 0 or it's higher than it should be, then we should simply return. Okay, this one fixed it. And I know this was a lot to digest here, but it was necessary in order to make our Minesweeper game work. I promise this was the hardest bit. Now let's add the same code to our app, except our field is not a 5x5 grid. So we need slightly different numbers. So let's go to the utility file and let's add a new function for getting the index. This function will be later used in another scenario as well. So it should be exported. Here the difference is that the width and height of our grid is 10, so we want to make sure that the coordinates do not go beyond nine, which is ten minus one, because of the 0-based index. And because we have ten items in a row, you multiply the number of rows by ten. Then we also explort the get coordinates function and then we map our existing bombs array in the generateTiles function. I'm writing things in a bit shorter way instead of calculating the index on the neighboring tile and then check if the original array had a bomb at that index. I just do these two steps in one line. So there we go. Now or generateTiles function is providing our metadata in a new format. But that obviously messes up things because our time component is still expecting the number instead of an object. So let's fix that. 12. Computed properties: Let's go back to Tiles.vue and fix up a few things. First of all, our prop is an object now. But what do we show now that we have two separate properties? If the tile contains a bomb, it should show a bomb. Otherwise, if the tile is surrounded by bombs, we should show how many bonds are around. This is a good scenario for a computer property. Computer properties look like methods, but act like props. Each computer property is a function that calculates the value usually based on a prop or data, and then we can use this value as if it was a prop. So let's go to our tile component and define a computed property called content. If the tile contains a bomb, it should return a bomb emoji. And if it contains an indicator of surrounding bombs, then we should show how many bombs and around. Remember to use the this keyword when referring to these variables. We can't stop here though, computer properties should always return something. In this case, if there's no bomb nor surrounding bombs, we should return an empty string. Now, this computer property content can be referred in our template. And now our grid looks much better. Now that we have a bit more text both in the header and in our tiles, let's quickly change the font family. We want to change this everywhere. So we go to the app.vue file and change the font family there. To make things even nicer. And other places where we can use a computer property is to color the numbers on the board. So let's create a computer property called color and return a color for each possible value. If there is one surrounding bomb, then it should be blue. If two than green. If three then red. Four should be purple. Five is brown. Six is turqouse, seven is black, and for eight and any other case grey. Now, this computer property should be used to affect the CSS. But unlike in the template, we cannot use computer properties or in fact editor variable coming from the script in CSS. What we can do though instead that inline style tag to our div, where we bind our CSS color property to our computed property. Now it looks almost like a game. We just have to make it interactive. 13. Flagging a tile: In this part, we add the option to flag the tiles. We have to change a few things to make flagging work. First of all, our data model needs to be able to store if a tile is flagged or not. For this, we should go to our generateTiles function and extend the generated tiles object with a new flagged property that we'll be false by default. Then, we should smarten up our tiles component to handle this flag. If a tile is flagged than it should show this flag above everything else so in the function that provides the content property, we should add an if condition to show a flag. If the tile is flagged. Then, we should define a method that flags or unflags a tile. Here we set the flag property of the tile to the opposite value. Now we should link this to an event handler the same way as we did with the reset button. We still listen to the click event. But we add the modifier to listen to the right-click instead of the left click by adding .right. Now if you click a tile, it should work, but it has an unwanted side effects. The context menu comes up. To avoid this, we could use prevent default in the method. But this pattern is so common that Vue created a shorthand for it. We can add another modifier .prevent, to avoid showing the context menu. Now that we can flag things, we can also count how many bombs the player should still fly to finish the game. We can get this in a new calculated, property in the App.vue file. That calculates how many flag types do we have in total, then extracts the result from the total number of bombs. The total number of bombs we defined already in our utility file. And my editor was smart enough to automatically import it. But if yours did not, please make sure that you exported it from the utility file and it is imported at the top of your app script. Now that we have the bombsRemaining computer property. We can bind it to its rightful place, with double curly brackets. Now, if you start flagging tiles the number of remaining bombs, starts to decrease. Lets move on which revealing ties. 14. Revealing a tile: Let's move on with revealing the tiles. For now, all the ties were revealed by default to make development easier, but that's going to change. Revealing the tiles will be similar to flagging the tiles, but it will have its differences. We start the same way. We extend our tiles objects in the generateTiles function with a new property revealed. Then let's go to the tiles component. Let's not hide the content of the tiles yet, but to indicate if they are revealed or not, let's add a background color that depends on the revealed flag. It will be a new computer property called backgroundColor that will return a light shade of grey if the tile has been revealed. And a white otherwise. We can add this to our style binding next to color to set the background color property of CSS. You might notice that the background-color property in CSS is usually written with a dash in between the words. And now we are using lower camelcase. But remember here we are binding a JavaScript object to the style attribute. And in JavaScript, we don't use dash in variable names. So that's why I have different syntax. But don't worry, it will work. Now let's move on to event handling. Let's start the other way around and add the event handler first. We are going to react to the click event again, no modifiers needed, and we link it to a reveal method. Then we defined a reveal method the same way as we did with flagging. But here there's going to be a difference. We could change a revealed flag here, but only for this tile. In Minesweeper if you click a tile, that hides no bombs, and has no surrounding bombs either, then not only the clicked tile should be revealed, but its neighbors as well. And then, the neighbors of those neighbors, if they also fit the criteria and so on. In tile component, we can do that. Tile component instances have simply no access to their neighbors. And who has access to the other tiles? The App component. The general structure of components in Vue apps is top to bottom. Usually top-level components contain the data and the main logic of the app, and they pass on the necessary information to their children, who are rather visual, than smart. But what if they need to tell the App component that something happened? In that case, they can emit a Custom Event. Custom events have a name and their parents can react to these events just like with any other event handling we saw at the Reset button or at flagging tie. So we are the reveal method in the Tile component and write, this.$emit("reveal") Then in the App component? We add an event handler for our new custom event and link it to a method we're about to write. Here's another difference comparing to the previous event handlers. Earlier we only wrote the method's name at the event handler. Here we call the method and pass on the index of the tile as a parameter. We are going to need that to know which tie should they up reveal. As you're going to see, both syntaxes are working. Binding a method name only or binding method call with parameters. Now let's write this method. It starts with the obvious. We need to reveal the tile. For that, we first lookup the selected tile, then check if it's not already revealed, and if it's not ten, let's reveal it. At this phase, if you start clicking tiles, their background's should turn gray, but only the clicked tile's background, not the surrounding ones. Let's move on. If the tide does not contain a bomb and the number of surrounding bombs is 0, then we shouldn't reveal the surrounding tiles. This is going to be a recursive function. Working in a similar fashion as the generateTiles function does. First, we lookup the coordinate of the revealing tile, then get the index of the neighboring ties and reveal them as well. It's easy to run into an infinite loop or other problems here though, When we try to reveal a neighbor that does not exist. In case the index is undefined, let's just return straight away. Now that should work. If you click a bomb or number on the map only that tile should be revealed. But when you click and empty area, the whole area should be shown. You know what's next? We're going to hide the content of the tiles before they are revealed. For this, we got to the content property in the tile and return an empty string if the tile is nor flagged, nor revealed. Now our game looks almost ready. 15. Did we win already?: Okay, we arrived to the finishing touches, the game is almost fully functioning, but we still miss the logic that modifies the player if the game was won or lost. We're going to derive the game status with computed properties. First, let's start with the game failed computer property in the App.vue file. If you revealed any tile that contains a bomb then we last the game. We can check this by going through the tiles with a find. And if it returns anything for this criteria, then we failed. Then let's create another computed property that tells us if we won the game. We win the game. If we revealed all the ties, that do not contain a bomb. First, we need to count how many tiles did we reveal. Then add the total number of bombs. And that should equal to the total number of tiles, which is 100 in our case. If this condition is true, then we've won the game. Now let us derive a game status out of these two computed properties. This will tell us which smile to show in the header. You can create computer properties on top of other computer properties. Just remember that you need to use the this keyword when referring to them. So let's create a computed property called gameStatus then return a sad face if the game failed. A smile with sunglasses if the game is won. And the regular smiley in any other case. Now we can bind this computer property to the header. If you fail the game, then we're going to see the sad face in the header. But if you win now you can feel like a rock star. 16. Every second counts: We arrived to the final bit, the timer. We said that this is going to be a separate component. So let's create a new file Timer.vue. Its template is going to be very simple, just a div that shows how many seconds have passed. We decided to make the timer a separate component because it has its own data and own logic. The seconds variable, for instance, would not be needed in the context of the whole app. So that's why we have it here in the timer component's own data option. By default, it will be 0, that's clear, but how should it change? The timer should start counting when the game has started, and it should stop once we failed or won the game. In other terms, the timer should count if the game is in progress. Let's assume that there's a computer property that tells us if the game is in progress and the timer component has access to it, we should start a timer if this property is true and stop it when it turns false. In short, we have to watch out for the changes of this prop. This is what watchers do. They watch data, computed property or a prop and once they change, they trigger a function. The function's name should be the same as the name of the washed variable. In this example, age is an incoming prop for this component. And the function watching out for his changes is also called age. Watcher functions also received the new value of the change variable in an attribute. Let's put this to use. First, let's use this new timer component in our app. Then let's create a computed property that tells us if the game is in progress or not. If the game is already won or failed, then it should return false. Otherwise, if there are tiles revealed, then true, the game is in progress, and if that condition is not true either, then return false, the game haven't even started. The last pass on this computer property to Timer. On the other side in the timer component we register that this component requires a prop called gamInProgress and we write a watcher for it. If it's value turned to true then first let's reset the timer, then create an interval that increases the timer every second. But this is only one side of it. We also have to clear this interval once the game stops. For this first, we save the intervals ID when we create it. This is going to be in the data as well. Everything that's changing and we need to store it for later, is defined in data. Then back in the watcher, if the value you did not turn to true but to false then let's clear this interval. Okay, we have a Mineswpper game that has all the major functions. Let's review what we did during this course and what we're still missing. 17. Review & Next steps: Let's review what we learned. We learned how to create a project which you Vue CLI. Dan, we learned that Vue a component-based library where each component has its own template, logic and style. We learned how to add a child to a component and how to pass on a prop to it. We learned about data storing variables in our components. We learned a directive that generates multiple components based on an array. We learn about methods that can manipulate our data and how to bind these methods to event handlers. We learned how the children can trigger customer events to their parents to notify them about changes. We learned about computed properties that can derive new information out of existing values. And finally, we learned about watchers who can react to changes of variables. Oh, and by the way, we created the Minesweeper game. It has its flaws and of course the design could be improved. But I'll let you be creative with that. Feel free to add your own style. Here's a few challenges for you though. We left a few bugs in the game that you could try to fix. In the current game, you can flag a tile even if it has been already revealed. And you can reveal a tile that has already been flagged. This should be an easy fix. The game also doesn't stop oOnce you failed. You can keep on revealing or flagging ties. That's a bit harder to fix. The fail scenario could be improved in other ways as well. Maybe highlight the clicked bomb in red, or if there were any tiles that were wrongly flagged, they should be highlighted in red as well. And finally, the timer does not reset if you reset the whole board. That also shouldn't be too hard to fix. You could also experiment with different grid sizes or maybe even add a selector for difficulty. You can find a more advanced version of this game down in the links. So good luck on your journey on learning Vue JS and making the best apps out there. I can't wait to see what you did there. Oh, and don't forget to check out the bonus video that explains how to put your game on the web and share it with your friends. 18. Bonus: Share your game: I'm sure you improve their game in many ways after the course. Now time to brag about it and share it with your friends. Here I'm showing you a free and easy way to put it on the web so you can share it. First of all, we need to create a version of our game that we can run in the browser. So far, we were generating a live preview of the game by running npm run serve in the background. But once you stopped that script, the game is gone. If you open the README, you see another command that complies and minifies your files for production. Before running this script, first, we need a short configuration that says the public paths to relative. Then we can run the script. This will generate files that contain everything we built so far. If you open them, they will probably look like gibberish because they are not meant for humans. These fires can run now though in a browser simply by double-clicking on index.html, without any script in the background. Okay, how do we put this on the web? I suggest to use GitHub pages because it's for free. First, you need the GitHub account if you don't already have one. Make sure you have a good username, because your username will be part of the URL where your game will be, then you should create a repository that will contain our game. Here naming counts again, it should be either your username.github.io, and then your game, we'll be available under the same URL. Or you can give it an arbitrary name, like minesweeper. And then the game will be available. under your username.github.io/minesweeper I suggest the second option, because to your primary URL, you might want to applaud your portfolio or main website later on. Then upload your files. If you're familiar with Git, you can upload your project with git. But if not, you can also manually upload your files, with the add files button. There you have to give a commit message, click the big green button, and it's uploaded. It should look something like this. Then go to your repository settings, scroll down and made sure that GitHub Pages is pointing to the location where your build is. Here sometimes you have to wait for a minute, but then you can just open the link and play.