{"__v":10,"_id":"5509775eaa9bd525001a0649","category":{"__v":8,"_id":"550974cc368a561700414759","project":"55070e814bb83b2500ec9404","version":"550974cb368a561700414757","pages":["550976bead1f0523008ecbc3","550976f1ad1f0523008ecbc6","550977144c7c3f2300aabef3","55097741ad1f0523008ecbc8","5509775eaa9bd525001a0649","5509778b4c7c3f2300aabef5","550977974c7c3f2300aabef7","5519449eeaa2ed2f0021348d"],"reference":false,"createdAt":"2015-03-18T11:08:01.925Z","from_sync":false,"order":1,"slug":"introduction","title":"Introduction"},"project":"55070e814bb83b2500ec9404","user":"55070d24d30b3f190011b941","version":{"__v":1,"_id":"550974cb368a561700414757","forked_from":"55070e814bb83b2500ec9407","project":"55070e814bb83b2500ec9404","createdAt":"2015-03-18T12:51:23.709Z","releaseDate":"2015-03-18T12:51:23.709Z","categories":["550974cc368a561700414758","550974cc368a561700414759","550974cc368a56170041475a","550974cc368a56170041475b","550974cc368a56170041475c","550974cc368a56170041475d","550974cc368a56170041475e"],"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.4.0","version":"1.4"},"updates":[],"createdAt":"2015-03-18T13:02:22.835Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"order":4,"body":"[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Testing Your Install\"\n}\n[/block]\nLet's make sure we're all on the same page. I'm going to assume that you've\nalready [downloaded the latest version of CFWheels](/page/download) and have it installed on your system. If you haven't done that, stop and read the [Installation](doc:installation) \nchapter and get everything setup. It's okay, this web page will wait for you.\n\nOkay, so you have CFWheels installed and can see the CFWheels \"Congratulations!\"\npage as shown  below. That wasn't that hard now, was it?\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://www.filepicker.io/api/file/856D11UBT9ifH7cPkDk0\",\n        \"Congrats.png\",\n        \"1017\",\n        \"649\",\n        \"#832229\",\n        \"\"\n      ],\n      \"caption\": \"Figure 1: Wheels congratulations screen\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Hello World: Your First CFWheels App\"\n}\n[/block]\nOkay, let's get to some example code. We know that you've been dying to get your\nhands on some code!\n\nTo continue with Programming Tutorial Tradition, we'll create the ubiquitous\n_Hello World!_ application. But to keep things interesting, let's add a little\nCFWheels magic along the way.\n\n## Setting up the Controller\n\nLet's create a controller from scratch to illustrate how easy it is to set up a\ncontroller and plug it into the CFWheels framework.\n\nFirst, create a file called `Say.cfc` in the `controllers` directory and add the\ncode below to the file.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfcomponent extends=\\\"Controller\\\">\\n</cfcomponent>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nCongratulations, you just created your first CFWheels controller! What does this\ncontroller do, you might ask? Well, to be honest, not much. It has no methods\ndefined, so it doesn't add any new functionality to our application. But because\nit extends the base `Controller` component, it inherits quite a bit of powerful\nfunctionality and is now tied into our CFWheels application.\n\nSo what happens if we try to call our new controller right now? Lets take a\nlook. Open your browser and point your browser to the new controller. Because my\nserver is installed on port 80, my URL is `http://cfwheels.1.0/index.cfm/say`.\nYou may need to enter a different URL, depending on how your web server is\nconfigured. In my case, I will be using the host name `cfwheels.1.0`, so my URL\nwill look like this:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://www.filepicker.io/api/file/BYTt7tphTYajK44jigYP\",\n        \"no-action-error.jpg\",\n        \"590\",\n        \"379\",\n        \"#86221c\",\n        \"\"\n      ],\n      \"caption\": \"Figure 2: Wheels error after setting up your blank say controller\"\n    }\n  ]\n}\n[/block]\nThe error says \"Could not find the view page for the 'index' action in the 'say'\ncontroller.\" Where did \"index\" come from? The URL we typed in only specified a\ncontroller name but no action. When an action is not specified in the URL,\nCFWheels assumes that we want the default action. Out of the box, the default\naction in CFWheels is set to `index`. So in our example, CFWheels tried to find\nthe `index` action within the `say` controller, and it threw an error because it\ncouldn't find its view page.\n\n## Setting up an Action\n\nBut let's jump ahead. Now that we have the controller created, let's add an\naction to it called `hello`. Change your `say` controller so it looks like the\ncode block below:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfcomponent extends=\\\"Controller\\\">\\n    <cffunction name=\\\"hello\\\"></cffunction>\\n</cfcomponent>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nAs you can see, we created an empty method named `hello`.\n\nNow let's call our new action in the browser and see what we get. To call the\n`hello` action, we simply add `/hello` to the end of the previous URL that we\nused to call our `say` controller:\n\n    http://cfwheels.1.0/index.cfm/say/hello\n\nOnce again, we get a ColdFusion error. Although we have created the controller\nand added the `hello` action to it, we haven't created the view.\n\n## Setting up the View\n\nBy default, when an action is called, CFWheels will look for a view file with\nthe same name as the action. It then hands off the processing to the view to\ndisplay the user interface. In our case, CFWheels tried to find a view file for\nour `say/hello` action and couldn't find one.\n\nLet's remedy the situation and create a view file. View files are simple CFML\npages that handle the output of our application. In most cases, views will\nreturn HTML code to the brower. By default, the view files will have the same\nname as our controller actions and will be grouped into a directory under the\nview directory. This new directory will have the same name as our controller.\n\nFind the `views` directory in the root of your CFWheels installation. There will\nbe a few directories in there already. For now, we need to create a new\ndirectory in the `views` directory called `say`. This is the same name as the\ncontroller that we created above.\n\nNow inside the `say` directory, create a file called `hello.cfm`. In the\n`hello.cfm` file, add the following line of code:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<h1>Hello World!</h1>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nSave your `hello.cfm` file, and let's call our `say/hello` action once again.\nYou have your first working CFWheels page if your browser looks like _Figure 3_\nbelow.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://www.filepicker.io/api/file/6HczCoSEG19ZD6mTz82w\",\n        \"working-hello.jpg\",\n        \"584\",\n        \"376\",\n        \"#e8e8e7\",\n        \"\"\n      ],\n      \"caption\": \"Figure 3: Your first working CFWheels action.\"\n    }\n  ]\n}\n[/block]\nYou have just created your first functional CFWheels page, albeit it is a very\nsimple one. Pat yourself on the back, go grab a snack, and when you're ready,\nlet's go on and extend the functionality of our _Hello World!_ application a\nlittle more.\n\n## Adding Dynamic Content to Your View\n\nWe will add some simple dynamic content to our `hello` action and add a second\naction to the application. We'll then use some CFWheels code to tie the 2\nactions together. Let's get get to it!\n\n### The Dynamic Content\n\nThe first thing we are going to do is to add some dynamic content to our\n`say/hello` action. Modify your `say` controller so it looks like the code block\nbelow:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfcomponent extends=\\\"Controller\\\">\\n\\n  <cffunction name=\\\"hello\\\">\\n    <cfset time = Now()>\\n  </cffunction>\\n\\n</cfcomponent><cfcomponent extends=\\\"Controller\\\">\\n\\n  <cffunction name=\\\"hello\\\">\\n    <cfset time = Now()>\\n  </cffunction>\\n\\n</cfcomponent>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nAll we are doing here is creating a variable called `time` and setting its value\nto the current server time using the basic ColdFusion `Now()` function. When we\ndo this, the variable becomes immediately available to our view code.\n\nWhy not just set up this value directly in the view? If you think about it,\nmaybe the logic behind the value of time may eventually change. What if\neventually we want to display its value based on the user's time zone? What if\nlater we decide to pull it from a web service instead? Remember, the controller\nis supposed to coordinate all of the data and business logic, not the view.\n\n### Displaying the Dynamic Content\n\nNext, we will modify our `say/hello.cfm` view file so that it looks like the\ncode block bellow. When we do this, the value will be displayed in the browser.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<h1>Hello World!</h1>\\n<p>Current time: <cfoutput>#time#</cfoutput></p>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nNow call your `say/hello` action again in your browser. Your browser should look\nlike _Figure 4_ below.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://www.filepicker.io/api/file/P8mNcbuHQBCcrDqe8q8y\",\n        \"hello-with-time.jpg\",\n        \"584\",\n        \"376\",\n        \"#e7e7e7\",\n        \"\"\n      ],\n      \"caption\": \"Figure 4: Hello World with the current date and time\"\n    }\n  ]\n}\n[/block]\nThis simple example showed that any dynamic content created in a controller\naction is available to the corresponding view file. In our application, we\ncreated a `time` variable in the `say/hello` controller action and display that\nvariable in our `say/hello.cfm` view file.\n\n## Adding a Second Action: Goodbye\n\nNow we will expand the functionality of our application once again by adding a\nsecond action to our `say` controller. If you feel adventurous, go ahead and add\na `goodbye` action to the `say` controller on your own, then create a\n`goodbye.cfm` view file that displays a \"Goodbye\" message to the user. If you're\nnot feeling that adventurous, we'll quickly go step by step.\n\nFirst, modify the the `say` controller file so that it looks like the code block\nbelow. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<cfcomponent extends=\\\"Controller\\\">\\n\\n  <cffunction name=\\\"hello\\\">\\n    <cfset time = Now()>\\n  </cffunction>\\n\\n  <cffunction name=\\\"goodbye\\\">\\n  </cffunction>\\n\\n</cfcomponent>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nNow go to the `views/say` directory and create a `goodbye.cfm` page.\n\nAdd the following code to the `goodbye.cfm` page and save it.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<h1>Goodbye World!</h1>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nIf we did everything right, we should be able to call the new `say/goodbye`\naction using the following URL:\n\n    http://cfwheels.1.0/index.cfm/say/goodbye\n\nYour browser should look like _Figure 5_ below:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://www.filepicker.io/api/file/72OV7NkpSb29IE70fTcz\",\n        \"working-goodbye.jpg\",\n        \"584\",\n        \"376\",\n        \"#e8e8e7\",\n        \"\"\n      ],\n      \"caption\": \"Figure 5: Your new goodbye action.\"\n    }\n  ]\n}\n[/block]\n## Linking to Other Actions\n\nNow let's link our two actions together. We will do this by adding a link to the\nbottom of each page so that it calls the other page.\n\n### Linking Hello to Goodbye\n\nOpen the `say/hello.cfm` view file. We are going to add a line of code to the\nend of this file so our `say/hello.cfm` view file looks like the code block\nbelow:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<h1>Hello World!</h1>\\n<p>Current time: <cfoutput>#time#</cfoutput></p>\\n<p>Time to say <cfoutput>#linkTo(text=\\\"goodbye\\\", action=\\\"goodbye\\\")#?</cfoutput></p>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nThe [linkTo()](doc:linkto) function is a built-in CFWheels function. In this case, we\nare passing 2 named parameters to it. The first parameter, `text`, is the text\nthat will be displayed in the hyperlink. The second parameter, `action`, defines\nthe action to point the link to. By using this built-in function, your\napplication's main URL may change, and even controllers and actions may get\nshifted around, but you won't suffer from the dreaded dead link. CFWheels will\nalways create a valid link for you as long as you configure it correctly when\nyou make infrastructure changes to your application.\n\nOnce you have added the additional line of code to the end of the\n`say/hello.cfm` view file, save your file and call the `say/hello` action from\nyour browser. Your browser should look like _Figure 6_ below.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://www.filepicker.io/api/file/QueHYHyRVupA4aHnRCCc\",\n        \"hello-with-goodbye-link.jpg\",\n        \"584\",\n        \"376\",\n        \"#e7e7e7\",\n        \"\"\n      ],\n      \"caption\": \"Figure 6: Your say/hello action with a link to the goodbye action\"\n    }\n  ]\n}\n[/block]\nYou can see that CFWheels created a link for us and added an appropriate URL for\nthe `say/goodbye` action to the link.\n\n### Linking Goodbye to Hello\n\nLet's complete our little app and add a corresponding link to the bottom of our\n`say/goodbye.cfm` view page.\n\nOpen your `say/goodbye.cfm` view page and modify it so it looks like the code\nblock below.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<h1>Goodbye World!</h1>\\n<p>Time to say <cfoutput>#linkTo(text=\\\"hello\\\", action=\\\"hello\\\")#?</cfoutput></p>\",\n      \"language\": \"text\"\n    }\n  ]\n}\n[/block]\nIf you now call the `say/goodbye` action in your browser, your browser should\nlook like _Figure 7_ below.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://www.filepicker.io/api/file/o9Sj8zK6S2GEboUkfXLw\",\n        \"goodbye-with-hello-link.jpg\",\n        \"584\",\n        \"376\",\n        \"#e7e7e7\",\n        \"\"\n      ],\n      \"caption\": \"Figure 7: Your say/goodbye action with a link to the hello action\"\n    }\n  ]\n}\n[/block]\n## Much More to Learn\n\nYou now know enough to be dangerous with CFWheels. Look out! But there are many\nmore powerful features to cover. You may have noticed that we haven't even\ntalked about the M in MVC.\n\nNo worries. We will get there. And we think you will enjoy it.","excerpt":"In this tutorial, we'll be writing a simple application to make sure we have\nCFWheels installed properly and that everything is working as it should. Along\nthe way, you'll get to know some basics about how applications built on top of\nCFWheels work.","slug":"beginner-tutorial-hello-world","type":"basic","title":"Beginner Tutorial: Hello World"}

Beginner Tutorial: Hello World

In this tutorial, we'll be writing a simple application to make sure we have CFWheels installed properly and that everything is working as it should. Along the way, you'll get to know some basics about how applications built on top of CFWheels work.

[block:api-header] { "type": "basic", "title": "Testing Your Install" } [/block] Let's make sure we're all on the same page. I'm going to assume that you've already [downloaded the latest version of CFWheels](/page/download) and have it installed on your system. If you haven't done that, stop and read the [Installation](doc:installation) chapter and get everything setup. It's okay, this web page will wait for you. Okay, so you have CFWheels installed and can see the CFWheels "Congratulations!" page as shown below. That wasn't that hard now, was it? [block:image] { "images": [ { "image": [ "https://www.filepicker.io/api/file/856D11UBT9ifH7cPkDk0", "Congrats.png", "1017", "649", "#832229", "" ], "caption": "Figure 1: Wheels congratulations screen" } ] } [/block] [block:api-header] { "type": "basic", "title": "Hello World: Your First CFWheels App" } [/block] Okay, let's get to some example code. We know that you've been dying to get your hands on some code! To continue with Programming Tutorial Tradition, we'll create the ubiquitous _Hello World!_ application. But to keep things interesting, let's add a little CFWheels magic along the way. ## Setting up the Controller Let's create a controller from scratch to illustrate how easy it is to set up a controller and plug it into the CFWheels framework. First, create a file called `Say.cfc` in the `controllers` directory and add the code below to the file. [block:code] { "codes": [ { "code": "<cfcomponent extends=\"Controller\">\n</cfcomponent>", "language": "text" } ] } [/block] Congratulations, you just created your first CFWheels controller! What does this controller do, you might ask? Well, to be honest, not much. It has no methods defined, so it doesn't add any new functionality to our application. But because it extends the base `Controller` component, it inherits quite a bit of powerful functionality and is now tied into our CFWheels application. So what happens if we try to call our new controller right now? Lets take a look. Open your browser and point your browser to the new controller. Because my server is installed on port 80, my URL is `http://cfwheels.1.0/index.cfm/say`. You may need to enter a different URL, depending on how your web server is configured. In my case, I will be using the host name `cfwheels.1.0`, so my URL will look like this: [block:image] { "images": [ { "image": [ "https://www.filepicker.io/api/file/BYTt7tphTYajK44jigYP", "no-action-error.jpg", "590", "379", "#86221c", "" ], "caption": "Figure 2: Wheels error after setting up your blank say controller" } ] } [/block] The error says "Could not find the view page for the 'index' action in the 'say' controller." Where did "index" come from? The URL we typed in only specified a controller name but no action. When an action is not specified in the URL, CFWheels assumes that we want the default action. Out of the box, the default action in CFWheels is set to `index`. So in our example, CFWheels tried to find the `index` action within the `say` controller, and it threw an error because it couldn't find its view page. ## Setting up an Action But let's jump ahead. Now that we have the controller created, let's add an action to it called `hello`. Change your `say` controller so it looks like the code block below: [block:code] { "codes": [ { "code": "<cfcomponent extends=\"Controller\">\n <cffunction name=\"hello\"></cffunction>\n</cfcomponent>", "language": "text" } ] } [/block] As you can see, we created an empty method named `hello`. Now let's call our new action in the browser and see what we get. To call the `hello` action, we simply add `/hello` to the end of the previous URL that we used to call our `say` controller: http://cfwheels.1.0/index.cfm/say/hello Once again, we get a ColdFusion error. Although we have created the controller and added the `hello` action to it, we haven't created the view. ## Setting up the View By default, when an action is called, CFWheels will look for a view file with the same name as the action. It then hands off the processing to the view to display the user interface. In our case, CFWheels tried to find a view file for our `say/hello` action and couldn't find one. Let's remedy the situation and create a view file. View files are simple CFML pages that handle the output of our application. In most cases, views will return HTML code to the brower. By default, the view files will have the same name as our controller actions and will be grouped into a directory under the view directory. This new directory will have the same name as our controller. Find the `views` directory in the root of your CFWheels installation. There will be a few directories in there already. For now, we need to create a new directory in the `views` directory called `say`. This is the same name as the controller that we created above. Now inside the `say` directory, create a file called `hello.cfm`. In the `hello.cfm` file, add the following line of code: [block:code] { "codes": [ { "code": "<h1>Hello World!</h1>", "language": "text" } ] } [/block] Save your `hello.cfm` file, and let's call our `say/hello` action once again. You have your first working CFWheels page if your browser looks like _Figure 3_ below. [block:image] { "images": [ { "image": [ "https://www.filepicker.io/api/file/6HczCoSEG19ZD6mTz82w", "working-hello.jpg", "584", "376", "#e8e8e7", "" ], "caption": "Figure 3: Your first working CFWheels action." } ] } [/block] You have just created your first functional CFWheels page, albeit it is a very simple one. Pat yourself on the back, go grab a snack, and when you're ready, let's go on and extend the functionality of our _Hello World!_ application a little more. ## Adding Dynamic Content to Your View We will add some simple dynamic content to our `hello` action and add a second action to the application. We'll then use some CFWheels code to tie the 2 actions together. Let's get get to it! ### The Dynamic Content The first thing we are going to do is to add some dynamic content to our `say/hello` action. Modify your `say` controller so it looks like the code block below: [block:code] { "codes": [ { "code": "<cfcomponent extends=\"Controller\">\n\n <cffunction name=\"hello\">\n <cfset time = Now()>\n </cffunction>\n\n</cfcomponent><cfcomponent extends=\"Controller\">\n\n <cffunction name=\"hello\">\n <cfset time = Now()>\n </cffunction>\n\n</cfcomponent>", "language": "text" } ] } [/block] All we are doing here is creating a variable called `time` and setting its value to the current server time using the basic ColdFusion `Now()` function. When we do this, the variable becomes immediately available to our view code. Why not just set up this value directly in the view? If you think about it, maybe the logic behind the value of time may eventually change. What if eventually we want to display its value based on the user's time zone? What if later we decide to pull it from a web service instead? Remember, the controller is supposed to coordinate all of the data and business logic, not the view. ### Displaying the Dynamic Content Next, we will modify our `say/hello.cfm` view file so that it looks like the code block bellow. When we do this, the value will be displayed in the browser. [block:code] { "codes": [ { "code": "<h1>Hello World!</h1>\n<p>Current time: <cfoutput>#time#</cfoutput></p>", "language": "text" } ] } [/block] Now call your `say/hello` action again in your browser. Your browser should look like _Figure 4_ below. [block:image] { "images": [ { "image": [ "https://www.filepicker.io/api/file/P8mNcbuHQBCcrDqe8q8y", "hello-with-time.jpg", "584", "376", "#e7e7e7", "" ], "caption": "Figure 4: Hello World with the current date and time" } ] } [/block] This simple example showed that any dynamic content created in a controller action is available to the corresponding view file. In our application, we created a `time` variable in the `say/hello` controller action and display that variable in our `say/hello.cfm` view file. ## Adding a Second Action: Goodbye Now we will expand the functionality of our application once again by adding a second action to our `say` controller. If you feel adventurous, go ahead and add a `goodbye` action to the `say` controller on your own, then create a `goodbye.cfm` view file that displays a "Goodbye" message to the user. If you're not feeling that adventurous, we'll quickly go step by step. First, modify the the `say` controller file so that it looks like the code block below. [block:code] { "codes": [ { "code": "<cfcomponent extends=\"Controller\">\n\n <cffunction name=\"hello\">\n <cfset time = Now()>\n </cffunction>\n\n <cffunction name=\"goodbye\">\n </cffunction>\n\n</cfcomponent>", "language": "text" } ] } [/block] Now go to the `views/say` directory and create a `goodbye.cfm` page. Add the following code to the `goodbye.cfm` page and save it. [block:code] { "codes": [ { "code": "<h1>Goodbye World!</h1>", "language": "text" } ] } [/block] If we did everything right, we should be able to call the new `say/goodbye` action using the following URL: http://cfwheels.1.0/index.cfm/say/goodbye Your browser should look like _Figure 5_ below: [block:image] { "images": [ { "image": [ "https://www.filepicker.io/api/file/72OV7NkpSb29IE70fTcz", "working-goodbye.jpg", "584", "376", "#e8e8e7", "" ], "caption": "Figure 5: Your new goodbye action." } ] } [/block] ## Linking to Other Actions Now let's link our two actions together. We will do this by adding a link to the bottom of each page so that it calls the other page. ### Linking Hello to Goodbye Open the `say/hello.cfm` view file. We are going to add a line of code to the end of this file so our `say/hello.cfm` view file looks like the code block below: [block:code] { "codes": [ { "code": "<h1>Hello World!</h1>\n<p>Current time: <cfoutput>#time#</cfoutput></p>\n<p>Time to say <cfoutput>#linkTo(text=\"goodbye\", action=\"goodbye\")#?</cfoutput></p>", "language": "text" } ] } [/block] The [linkTo()](doc:linkto) function is a built-in CFWheels function. In this case, we are passing 2 named parameters to it. The first parameter, `text`, is the text that will be displayed in the hyperlink. The second parameter, `action`, defines the action to point the link to. By using this built-in function, your application's main URL may change, and even controllers and actions may get shifted around, but you won't suffer from the dreaded dead link. CFWheels will always create a valid link for you as long as you configure it correctly when you make infrastructure changes to your application. Once you have added the additional line of code to the end of the `say/hello.cfm` view file, save your file and call the `say/hello` action from your browser. Your browser should look like _Figure 6_ below. [block:image] { "images": [ { "image": [ "https://www.filepicker.io/api/file/QueHYHyRVupA4aHnRCCc", "hello-with-goodbye-link.jpg", "584", "376", "#e7e7e7", "" ], "caption": "Figure 6: Your say/hello action with a link to the goodbye action" } ] } [/block] You can see that CFWheels created a link for us and added an appropriate URL for the `say/goodbye` action to the link. ### Linking Goodbye to Hello Let's complete our little app and add a corresponding link to the bottom of our `say/goodbye.cfm` view page. Open your `say/goodbye.cfm` view page and modify it so it looks like the code block below. [block:code] { "codes": [ { "code": "<h1>Goodbye World!</h1>\n<p>Time to say <cfoutput>#linkTo(text=\"hello\", action=\"hello\")#?</cfoutput></p>", "language": "text" } ] } [/block] If you now call the `say/goodbye` action in your browser, your browser should look like _Figure 7_ below. [block:image] { "images": [ { "image": [ "https://www.filepicker.io/api/file/o9Sj8zK6S2GEboUkfXLw", "goodbye-with-hello-link.jpg", "584", "376", "#e7e7e7", "" ], "caption": "Figure 7: Your say/goodbye action with a link to the hello action" } ] } [/block] ## Much More to Learn You now know enough to be dangerous with CFWheels. Look out! But there are many more powerful features to cover. You may have noticed that we haven't even talked about the M in MVC. No worries. We will get there. And we think you will enjoy it.